From 5295d97476eb9593ff371264db6a661a768db4e7 Mon Sep 17 00:00:00 2001 From: EDR Date: Fri, 23 Sep 2016 10:33:46 +0200 Subject: [PATCH] Proposal for dynamic implementation and testing --- .../DynamicExpresso.Core.csproj | 1 + src/DynamicExpresso.Core/Parsing/Parser.cs | 36 ++++- .../DynamicExpresso.UnitTest.csproj | 1 + test/DynamicExpresso.UnitTest/DynamicTest.cs | 148 +++++++++--------- 4 files changed, 113 insertions(+), 73 deletions(-) diff --git a/src/DynamicExpresso.Core/DynamicExpresso.Core.csproj b/src/DynamicExpresso.Core/DynamicExpresso.Core.csproj index 3a1dcb12..afa4efab 100644 --- a/src/DynamicExpresso.Core/DynamicExpresso.Core.csproj +++ b/src/DynamicExpresso.Core/DynamicExpresso.Core.csproj @@ -36,6 +36,7 @@ 1591 + diff --git a/src/DynamicExpresso.Core/Parsing/Parser.cs b/src/DynamicExpresso.Core/Parsing/Parser.cs index 6f171dae..a87abaaf 100644 --- a/src/DynamicExpresso.Core/Parsing/Parser.cs +++ b/src/DynamicExpresso.Core/Parsing/Parser.cs @@ -1,5 +1,7 @@ -using System; +using Microsoft.CSharp.RuntimeBinder; +using System; using System.Collections.Generic; +using System.Dynamic; using System.Globalization; using System.Linq; using System.Linq.Expressions; @@ -850,8 +852,25 @@ Expression ParseMemberAccess(Type type, Expression instance) return GeneratePropertyOrFieldExpression(type, instance, errorPos, id); } + bool IsDynamic(Type type) + { + return type.IsAssignableFrom(typeof(IDynamicMetaObjectProvider)) || + type.IsAssignableFrom(typeof(ExpandoObject)); + } + Expression GeneratePropertyOrFieldExpression(Type type, Expression instance, int errorPos, string propertyOrFieldName) { + if (IsDynamic(type)) + { + var binder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember( + CSharpBinderFlags.None, + propertyOrFieldName, + type, + new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) } + ); + + return Expression.Dynamic(binder, typeof(object), instance); + } MemberInfo member = FindPropertyOrField(type, propertyOrFieldName, instance == null); if (member != null) { @@ -867,6 +886,21 @@ Expression ParseMethodInvocation(Type type, Expression instance, int errorPos, s { Expression[] args = ParseArgumentList(); + if (IsDynamic(type)) + { + var argsDynamic = args.ToList(); + argsDynamic.Insert(0, instance); + var binderM = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember( + CSharpBinderFlags.None, + methodName, + null, + type, + argsDynamic.Select(x => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)) + ); + + return Expression.Dynamic(binderM, typeof(object), argsDynamic); + } + var methodInvocationExpression = ParseNormalMethodInvocation(type, instance, errorPos, methodName, args); if (methodInvocationExpression == null && instance != null) diff --git a/test/DynamicExpresso.UnitTest/DynamicExpresso.UnitTest.csproj b/test/DynamicExpresso.UnitTest/DynamicExpresso.UnitTest.csproj index 56aa8e8a..09e877cd 100644 --- a/test/DynamicExpresso.UnitTest/DynamicExpresso.UnitTest.csproj +++ b/test/DynamicExpresso.UnitTest/DynamicExpresso.UnitTest.csproj @@ -32,6 +32,7 @@ 4 + diff --git a/test/DynamicExpresso.UnitTest/DynamicTest.cs b/test/DynamicExpresso.UnitTest/DynamicTest.cs index 4078b20a..ef0b78d5 100644 --- a/test/DynamicExpresso.UnitTest/DynamicTest.cs +++ b/test/DynamicExpresso.UnitTest/DynamicTest.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using Microsoft.CSharp.RuntimeBinder; +using NUnit.Framework; using System; using System.Collections.Generic; using System.Dynamic; @@ -8,75 +9,78 @@ namespace DynamicExpresso.UnitTest { -// [TestFixture] -// public class DynamicTest -// { -// [Test] -// public void Read_Property_of_an_ExpandoObject() -// { -// dynamic dyn = new ExpandoObject(); -// dyn.Foo = "bar"; -// -// var interpreter = new Interpreter() -// .SetVariable("dyn", dyn); -// -// Assert.AreEqual(dyn.Foo, interpreter.Eval("dyn.Foo")); -// } -// -// [Test] -// public void Invoke_Method_of_an_ExpandoObject() -// { -// dynamic dyn = new ExpandoObject(); -// dyn.Foo = new Func(() => "bar"); -// -// var interpreter = new Interpreter() -// .SetVariable("dyn", dyn); -// -// Assert.AreEqual(dyn.Foo(), interpreter.Eval("dyn.Foo()")); -// } -// -// [Test] -// public void Case_Insensitive_Dynamic_Members() -// { -// dynamic dyn = new ExpandoObject(); -// dyn.Bar = 10; -// -// var result = new Interpreter() -// .Eval("dyn.BAR", new Parameter("dyn", dyn)); -// -// Assert.AreEqual(10, result); -// } -// -// [Test] -// public void Test_With_Standard_Object() -// { -// var myInstance = DateTime.Now; -// -// var methodInfo = myInstance.GetType().GetMethod("ToUniversalTime"); -// -// var methodCallExpression = Expression.Call(Expression.Constant(myInstance), methodInfo); -// var expression = Expression.Lambda(methodCallExpression); -// -// Assert.AreEqual(myInstance.ToUniversalTime(), expression.Compile().DynamicInvoke()); -// } -// -// [Test] -// public void Test_With_Dynamic_Object() -// { -// dynamic myInstance = new ExpandoObject(); -// myInstance.MyMethod = new Func(() => "hello world"); -// -// var binder = Binder.InvokeMember( -// CSharpBinderFlags.None, -// "MyMethod", -// null, -// this.GetType(), -// new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) }); -// -// var methodCallExpression = Expression.Dynamic(binder, typeof(object), Expression.Constant(myInstance)); -// var expression = Expression.Lambda(methodCallExpression); -// -// Assert.AreEqual(myInstance.MyMethod(), expression.Compile().DynamicInvoke()); -// } -// } + [TestFixture] + public class DynamicTest + { + [Test] + public void Read_Property_of_an_ExpandoObject() + { + dynamic dyn = new ExpandoObject(); + dyn.Foo = "bar"; + + var interpreter = new Interpreter() + .SetVariable("dyn", dyn); + + Assert.AreEqual(dyn.Foo, interpreter.Eval("dyn.Foo")); + } + + [Test] + public void Invoke_Method_of_an_ExpandoObject() + { + dynamic dyn = new ExpandoObject(); + dyn.Foo = new Func(() => "bar"); + + var interpreter = new Interpreter() + .SetVariable("dyn", dyn); + + Assert.AreEqual(dyn.Foo(), interpreter.Eval("dyn.Foo()")); + } + + [Test] + public void Case_Insensitive_Dynamic_Members() + { + Assert.Throws(() => + { + dynamic dyn = new ExpandoObject(); + dyn.Bar = 10; + + var result = new Interpreter() + .Eval("dyn.BAR", new Parameter("dyn", dyn)); + + Assert.AreEqual(10, result); + }); + } + + [Test] + public void Test_With_Standard_Object() + { + var myInstance = DateTime.Now; + + var methodInfo = myInstance.GetType().GetMethod("ToUniversalTime"); + + var methodCallExpression = Expression.Call(Expression.Constant(myInstance), methodInfo); + var expression = Expression.Lambda(methodCallExpression); + + Assert.AreEqual(myInstance.ToUniversalTime(), expression.Compile().DynamicInvoke()); + } + + [Test] + public void Test_With_Dynamic_Object() + { + dynamic myInstance = new ExpandoObject(); + myInstance.MyMethod = new Func(() => "hello world"); + + var binder = Binder.InvokeMember( + CSharpBinderFlags.None, + "MyMethod", + null, + this.GetType(), + new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) }); + + var methodCallExpression = Expression.Dynamic(binder, typeof(object), Expression.Constant(myInstance)); + var expression = Expression.Lambda(methodCallExpression); + + Assert.AreEqual(myInstance.MyMethod(), expression.Compile().DynamicInvoke()); + } + } }