-using System;\r
-using System.Collections;\r
-using System.Collections.Generic;\r
-using System.Collections.ObjectModel;\r
-using System.Linq;\r
-using System.Text;\r
-using System.Reflection;\r
-using System.Collections.Specialized;\r
-using System.Linq.Expressions;\r
-\r
-namespace System.Linq.jvm\r
-{\r
- class ExpressionInterpreter : ExpressionVisitor\r
- {\r
- \r
- LambdaExpression _exp;\r
-\r
- object[] _args;\r
-\r
- object _value = null;\r
-\r
- public object Value\r
- {\r
- get { return _value; }\r
- }\r
-\r
- public ExpressionInterpreter(object[] args)\r
- {\r
- _args = args;\r
- }\r
-\r
-\r
- public void Run(LambdaExpression exp)\r
- {\r
- _exp = exp;\r
- Visit(exp.Body);\r
- }\r
-\r
- protected override void Visit(Expression expression)\r
- {\r
- if (expression == null)\r
- {\r
- return;\r
- }\r
- if (expression.NodeType == ExpressionType.Power)\r
- {\r
- VisitBinary((BinaryExpression)expression);\r
- }\r
- else\r
- {\r
- base.Visit(expression);\r
- }\r
- }\r
-\r
- private void VisitCoalesce(BinaryExpression binary)\r
- {\r
- Visit(binary.Left);\r
- if (_value == null)\r
- {\r
- Visit(binary.Right);\r
- }\r
- }\r
-\r
- private void VisitAndAlso(BinaryExpression binary)\r
- {\r
- object right = null;\r
- object left = null;\r
-\r
- Visit(binary.Left);\r
- right = _value;\r
-\r
- if (right == null || ((bool)right))\r
- {\r
- Visit(binary.Right);\r
- left = _value;\r
- }\r
-\r
- _value = Math.And(right, left);\r
- \r
- }\r
-\r
- private void VisitOrElse(BinaryExpression binary)\r
- {\r
- object right = null;\r
- object left = null;\r
-\r
- Visit(binary.Left);\r
- right = _value;\r
-\r
- if (right == null || !((bool)right))\r
- {\r
- Visit(binary.Right);\r
- left = _value;\r
- }\r
- _value = Math.Or(right, left);\r
- }\r
-\r
- private void VisitCommonBinary(BinaryExpression binary)\r
- {\r
- try\r
- {\r
- Visit(binary.Left);\r
- object left = _value;\r
- Visit(binary.Right);\r
- object right = _value;\r
-\r
- if (binary.Method != null)\r
- {\r
- _value = binary.Method.Invoke(null, new object[] { left, right });\r
- return;\r
- }\r
-\r
- TypeCode tc = Type.GetTypeCode(binary.Type);\r
-\r
- switch (binary.NodeType)\r
- {\r
- case ExpressionType.ArrayIndex:\r
- _value = ((Array)left).GetValue((int)right);\r
- return;\r
- case ExpressionType.Equal:\r
- _value = left.Equals(right);\r
- return;\r
- case ExpressionType.NotEqual:\r
- _value = !left.Equals(right);\r
- return;\r
- case ExpressionType.LessThan:\r
- _value = (Comparer.Default.Compare(left, right) < 0);\r
- return;\r
- case ExpressionType.LessThanOrEqual:\r
- _value = (Comparer.Default.Compare(left, right) <= 0);\r
- return;\r
- case ExpressionType.GreaterThan:\r
- _value = (Comparer.Default.Compare(left, right) > 0);\r
- return;\r
- case ExpressionType.GreaterThanOrEqual:\r
- _value = (Comparer.Default.Compare(left, right) >= 0);\r
- return;\r
- case ExpressionType.RightShift:\r
- _value = Math.RightShift(left, Convert.ToInt32(right), tc);\r
- return;\r
- case ExpressionType.LeftShift:\r
- _value = Math.LeftShift(left, Convert.ToInt32(right), tc);\r
- return;\r
- default:\r
- _value = Math.Evaluate(left, right, binary.Type, binary.NodeType);\r
- break;\r
-\r
- }\r
- }\r
- catch (OverflowException)\r
- {\r
- throw;\r
- }\r
- catch (Exception e)\r
- {\r
-\r
- throw new NotImplementedException(\r
- string.Format(\r
- "Interpriter for BinaryExpression with NodeType {0} is not implimented",\r
- binary.NodeType),\r
- e);\r
- }\r
- }\r
-\r
- protected override void VisitBinary(BinaryExpression binary)\r
- {\r
- switch (binary.NodeType)\r
- {\r
- case ExpressionType.AndAlso:\r
- VisitAndAlso(binary);\r
- return;\r
- case ExpressionType.OrElse:\r
- VisitOrElse(binary);\r
- return;\r
- case ExpressionType.Coalesce:\r
- VisitCoalesce(binary);\r
- return;\r
- default:\r
- VisitCommonBinary(binary);\r
- break;\r
- }\r
- }\r
-\r
- protected override void VisitUnary(UnaryExpression unary)\r
- {\r
- Visit(unary.Operand);\r
- object o = _value;\r
-\r
- if (unary.Method != null)\r
- {\r
- _value = unary.Method.Invoke(null, new object[] { o });\r
- return;\r
- }\r
-\r
- switch (unary.NodeType)\r
- {\r
- case ExpressionType.TypeAs:\r
- if (!Math.IsType(unary.Type, o))\r
- {\r
- _value = null;\r
- }\r
- return;\r
- case ExpressionType.ArrayLength:\r
- _value = ((Array)o).Length;\r
- return;\r
- case ExpressionType.Negate:\r
- _value = Math.Negete(o, Type.GetTypeCode(unary.Type));\r
- return;\r
- case ExpressionType.NegateChecked:\r
- _value = Math.NegeteChecked(o, Type.GetTypeCode(unary.Type));\r
- return;\r
- case ExpressionType.Not:\r
- _value = ! Convert.ToBoolean(o);\r
- return;\r
- case ExpressionType.UnaryPlus:\r
- _value = o;\r
- return;\r
- case ExpressionType.Convert:\r
- _value = Math.ConvertToTypeUnchecked(o, unary.Type);\r
- return;\r
- case ExpressionType.ConvertChecked:\r
- _value = Math.ConvertToTypeChecked(o, unary.Type);\r
- return;\r
- }\r
- throw new NotImplementedException(\r
- string.Format(\r
- "Interpriter for UnaryExpression with NodeType {0} is not implimented",\r
- unary.NodeType));\r
-\r
- }\r
-\r
- protected override void VisitNew(NewExpression nex)\r
- {\r
- if (nex.Constructor == null)\r
- {\r
- _value = System.Activator.CreateInstance(nex.Type);\r
- }\r
- else\r
- {\r
- object[] parameters = VisitListExpressions(nex.Arguments);\r
- _value = nex.Constructor.Invoke(parameters);\r
- }\r
- }\r
-\r
- protected override void VisitTypeIs(TypeBinaryExpression type)\r
- {\r
- Visit(type.Expression);\r
- _value = Math.IsType(type.TypeOperand, _value);\r
- }\r
-\r
- private void VisitMemberInfo(MemberInfo mi)\r
- {\r
- object o = _value;\r
- switch (mi.MemberType)\r
- {\r
- case MemberTypes.Field:\r
- _value = ((FieldInfo)mi).GetValue(o);\r
- return;\r
- case MemberTypes.Property:\r
- _value = ((PropertyInfo)mi).GetValue(o, null);\r
- return;\r
- }\r
-\r
- throw new NotImplementedException(\r
- string.Format(\r
- "Interpriter for MemberInfo with MemberType {0} is not implimented",\r
- mi.MemberType));\r
- }\r
-\r
- protected override void VisitMemberAccess(MemberExpression member)\r
- {\r
- Visit(member.Expression);\r
- VisitMemberInfo(member.Member);\r
- }\r
-\r
-\r
- protected override void VisitNewArray(NewArrayExpression newArray)\r
- {\r
- switch (newArray.NodeType)\r
- {\r
- case ExpressionType.NewArrayInit:\r
- VisitNewArrayInit(newArray);\r
- return;\r
- case ExpressionType.NewArrayBounds:\r
- VisitNewArrayBounds(newArray);\r
- return;\r
- }\r
- throw new NotImplementedException(\r
- string.Format(\r
- "Interpriter for VisitNewArray with NodeType {0} is not implimented",\r
- newArray.NodeType));\r
- }\r
-\r
- private void VisitNewArrayBounds(NewArrayExpression newArray)\r
- {\r
- int[] lengths = new int[newArray.Expressions.Count];\r
- for (int i = 0; i < lengths.Length; i++)\r
- {\r
- Visit(newArray.Expressions[i]);\r
- lengths[i] = (int)_value;\r
- }\r
- _value = Array.CreateInstance(newArray.Type.GetElementType(), lengths);\r
-\r
- }\r
-\r
- private void VisitNewArrayInit(NewArrayExpression newArray)\r
- {\r
- Array arr = Array.CreateInstance(\r
- newArray.Type.GetElementType(),\r
- newArray.Expressions.Count);\r
- for (int i = 0; i < arr.Length; i++)\r
- {\r
- Visit(newArray.Expressions[i]);\r
- arr.SetValue(_value, i);\r
- }\r
- _value = arr;\r
- }\r
-\r
- protected override void VisitConditional(ConditionalExpression conditional)\r
- {\r
- Visit(conditional.Test);\r
- if ((bool)_value)\r
- {\r
- Visit(conditional.IfTrue);\r
- }\r
- else\r
- {\r
- Visit(conditional.IfFalse);\r
- }\r
- }\r
-\r
- protected override void VisitMethodCall(MethodCallExpression call)\r
- {\r
- if (call.Object != null)\r
- {\r
- Visit(call.Object);\r
- }\r
- object callObject = _value;\r
- object[] arguments = VisitListExpressions(call.Arguments);\r
- _value = call.Method.Invoke(callObject, arguments);\r
- }\r
-\r
- protected override void VisitParameter(ParameterExpression parameter)\r
- {\r
- for (int i = 0; i < _exp.Parameters.Count; i++)\r
- {\r
- if (_exp.Parameters[i].Name.Equals(parameter.Name))\r
- {\r
- _value = _args[i];\r
- return;\r
- }\r
- }\r
- _value = null;\r
- }\r
-\r
- protected override void VisitConstant(ConstantExpression constant)\r
- {\r
- _value = constant.Value;\r
- }\r
-\r
- protected override void VisitInvocation(InvocationExpression invocation)\r
- {\r
- Visit(invocation.Expression);\r
- object o = _value;\r
- object[] arguments = VisitListExpressions(invocation.Arguments);\r
- _value = ((Delegate)o).DynamicInvoke(arguments);\r
- }\r
-\r
- protected override void VisitMemberListBinding(MemberListBinding binding)\r
- {\r
- object o = _value;\r
- try\r
- {\r
- VisitMemberInfo(binding.Member);\r
- base.VisitMemberListBinding(binding);\r
- }\r
- finally\r
- {\r
- _value = o;\r
- }\r
- }\r
-\r
- protected override void VisitElementInitializer(ElementInit initializer)\r
- {\r
- object o = _value;\r
- try\r
- {\r
- object[] arguments =\r
- VisitListExpressions(initializer.Arguments);\r
- initializer.AddMethod.Invoke(o, arguments);\r
-\r
- }\r
- finally\r
- {\r
- _value = o;\r
- }\r
- }\r
-\r
- protected override void VisitMemberMemberBinding(MemberMemberBinding binding)\r
- {\r
- object o = _value;\r
- try\r
- {\r
- VisitMemberInfo(binding.Member);\r
- base.VisitMemberMemberBinding(binding);\r
- }\r
- finally\r
- {\r
- _value = o;\r
- }\r
- }\r
-\r
- protected override void VisitMemberAssignment(MemberAssignment assignment)\r
- {\r
- object o = _value;\r
- try\r
- {\r
- Visit(assignment.Expression);\r
- switch (assignment.Member.MemberType)\r
- {\r
- case MemberTypes.Field:\r
- ((FieldInfo)assignment.Member).SetValue(o, _value);\r
- return;\r
- case MemberTypes.Property:\r
- ((PropertyInfo)assignment.Member).SetValue(o, _value, null);\r
- return;\r
- }\r
- throw new NotImplementedException(\r
- string.Format(\r
- "Interpriter for MemberExpression with MemberType {0} is not implimented",\r
- assignment.Member.MemberType));\r
- }\r
- finally\r
- {\r
- _value = o;\r
- }\r
- }\r
-\r
- private object[] VisitListExpressions(ReadOnlyCollection<Expression> collection)\r
- {\r
- object o = _value;\r
- try\r
- {\r
- object[] results = new object[collection.Count];\r
- for (int i = 0; i < results.Length; i++)\r
- {\r
- Visit(collection[i]);\r
- results[i] = _value;\r
- }\r
-\r
- return results;\r
- }\r
- finally\r
- {\r
- _value = o;\r
- }\r
- }\r
-\r
- }\r
-\r
-}\r
+//
+// ExpressionInterpreter.cs
+//
+// (C) 2008 Mainsoft, Inc. (http://www.mainsoft.com)
+// (C) 2008 db4objects, Inc. (http://www.db4o.com)
+// (C) 2010 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace System.Linq.jvm {
+
+ struct LambdaInfo {
+ public readonly LambdaExpression Lambda;
+ public readonly object [] Arguments;
+
+ public LambdaInfo (LambdaExpression lambda, object [] arguments)
+ {
+ this.Lambda = lambda;
+ this.Arguments = arguments;
+ }
+ }
+
+ class HoistedVariableDetector : ExpressionVisitor {
+
+ readonly Dictionary<ParameterExpression, LambdaExpression> parameter_to_lambda =
+ new Dictionary<ParameterExpression, LambdaExpression> ();
+
+ Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
+
+ LambdaExpression lambda;
+
+ public Dictionary<LambdaExpression, List<ParameterExpression>> Process (LambdaExpression lambda)
+ {
+ Visit (lambda);
+ return hoisted_map;
+ }
+
+ protected override void VisitLambda (LambdaExpression lambda)
+ {
+ this.lambda = lambda;
+ foreach (var parameter in lambda.Parameters)
+ parameter_to_lambda [parameter] = lambda;
+ base.VisitLambda (lambda);
+ }
+
+ protected override void VisitParameter (ParameterExpression parameter)
+ {
+ if (lambda.Parameters.Contains (parameter))
+ return;
+
+ Hoist (parameter);
+ }
+
+ void Hoist (ParameterExpression parameter)
+ {
+ LambdaExpression lambda;
+ if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
+ return;
+
+ if (hoisted_map == null)
+ hoisted_map = new Dictionary<LambdaExpression, List<ParameterExpression>> ();
+
+ List<ParameterExpression> hoisted;
+ if (!hoisted_map.TryGetValue (lambda, out hoisted)) {
+ hoisted = new List<ParameterExpression> ();
+ hoisted_map [lambda] = hoisted;
+ }
+
+ hoisted.Add (parameter);
+ }
+ }
+
+
+ class ExpressionInterpreter : ExpressionVisitor {
+
+ readonly Stack<LambdaInfo> lambdas = new Stack<LambdaInfo> ();
+ readonly Stack<object> stack = new Stack<object> ();
+
+ readonly Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
+ readonly Dictionary<ParameterExpression, object> hoisted_values;
+
+ void Push (object value)
+ {
+ stack.Push (value);
+ }
+
+ object Pop ()
+ {
+ return stack.Pop ();
+ }
+
+ public ExpressionInterpreter (LambdaExpression lambda)
+ {
+ hoisted_map = new HoistedVariableDetector ().Process (lambda);
+
+ if (hoisted_map != null)
+ hoisted_values = new Dictionary<ParameterExpression, object> ();
+ }
+
+ private void VisitCoalesce (BinaryExpression binary)
+ {
+ Visit (binary.Left);
+
+ var left = Pop ();
+
+ if (left == null) {
+ Visit (binary.Right);
+ return;
+ }
+
+ if (binary.Conversion == null) {
+ Push (left);
+ return;
+ }
+
+ Push (Invoke (binary.Conversion.Compile (this), new [] { left }));
+ }
+
+ void VisitAndAlso (BinaryExpression binary)
+ {
+ object left = null;
+ object right = null;
+
+ Visit (binary.Left);
+
+ left = Pop ();
+
+ if (left == null || ((bool) left)) {
+ Visit (binary.Right);
+ right = Pop ();
+ }
+
+ Push (Math.And (left, right));
+ }
+
+ void VisitUserDefinedAndAlso (BinaryExpression binary)
+ {
+ object left = null;
+ object right = null;
+
+ Visit (binary.Left);
+
+ left = Pop ();
+
+ if (InvokeFalseOperator (binary, left)) {
+ Push (left);
+ return;
+ }
+
+ Visit (binary.Right);
+ right = Pop ();
+
+ if (binary.IsLiftedToNull && right == null) {
+ Push (null);
+ return;
+ }
+
+ Push (InvokeMethod (binary.Method, null, new [] { left, right }));
+ }
+
+ static bool InvokeTrueOperator (BinaryExpression binary, object target)
+ {
+ return (bool) InvokeMethod (GetTrueOperator (binary), null, new [] { target });
+ }
+
+ static bool InvokeFalseOperator (BinaryExpression binary, object target)
+ {
+ return (bool) InvokeMethod (GetFalseOperator (binary), null, new [] { target });
+ }
+
+ static MethodInfo GetFalseOperator (BinaryExpression binary)
+ {
+ return Expression.GetFalseOperator (binary.Left.Type.GetNotNullableType ());
+ }
+
+ static MethodInfo GetTrueOperator (BinaryExpression binary)
+ {
+ return Expression.GetTrueOperator (binary.Left.Type.GetNotNullableType ());
+ }
+
+ void VisitOrElse (BinaryExpression binary)
+ {
+ object left = null;
+ object right = null;
+
+ Visit (binary.Left);
+ left = Pop ();
+
+ if (left == null || !((bool) left)) {
+ Visit (binary.Right);
+ right = Pop ();
+ }
+
+ Push (Math.Or (left, right));
+ }
+
+ void VisitUserDefinedOrElse (BinaryExpression binary)
+ {
+ object left = null;
+ object right = null;
+
+ Visit (binary.Left);
+ left = Pop ();
+
+ if (InvokeTrueOperator (binary, left)) {
+ Push (left);
+ return;
+ }
+
+ Visit (binary.Right);
+ right = Pop ();
+
+ if (binary.IsLiftedToNull && right == null) {
+ Push (null);
+ return;
+ }
+
+ Push (InvokeMethod (binary.Method, null, new [] { left, right }));
+ }
+
+ void VisitLogicalBinary (BinaryExpression binary)
+ {
+ Visit (binary.Left);
+ Visit (binary.Right);
+
+ var right = Pop ();
+ var left = Pop ();
+
+ Push (Math.Evaluate (left, right, binary.Type, binary.NodeType));
+ }
+
+ void VisitArithmeticBinary (BinaryExpression binary)
+ {
+ Visit (binary.Left);
+ Visit (binary.Right);
+
+ if (IsNullBinaryLifting (binary))
+ return;
+
+ var right = Pop ();
+ var left = Pop ();
+
+ switch (binary.NodeType) {
+ case ExpressionType.RightShift:
+ Push (Math.RightShift (left, Convert.ToInt32 (right), Type.GetTypeCode (binary.Type.GetNotNullableType ())));
+ return;
+ case ExpressionType.LeftShift:
+ Push (Math.LeftShift (left, Convert.ToInt32 (right), Type.GetTypeCode (binary.Type.GetNotNullableType ())));
+ return;
+ default:
+ Push (Math.Evaluate (left, right, binary.Type, binary.NodeType));
+ break;
+ }
+ }
+
+ bool IsNullRelationalBinaryLifting (BinaryExpression binary)
+ {
+ var right = Pop ();
+ var left = Pop ();
+
+ if (binary.IsLifted && (left == null || right == null)) {
+ if (binary.IsLiftedToNull) {
+ Push (null);
+ return true;
+ }
+
+ switch (binary.NodeType) {
+ case ExpressionType.Equal:
+ Push (BinaryEqual (binary, left, right));
+ break;
+ case ExpressionType.NotEqual:
+ Push (BinaryNotEqual (binary, left, right));
+ break;
+ default:
+ Push (false);
+ break;
+ }
+
+ return true;
+ }
+
+ Push (left);
+ Push (right);
+
+ return false;
+ }
+
+ void VisitRelationalBinary (BinaryExpression binary)
+ {
+ Visit (binary.Left);
+ Visit (binary.Right);
+
+ if (IsNullRelationalBinaryLifting (binary))
+ return;
+
+ var right = Pop ();
+ var left = Pop ();
+
+ switch (binary.NodeType) {
+ case ExpressionType.Equal:
+ Push (BinaryEqual (binary, left, right));
+ return;
+ case ExpressionType.NotEqual:
+ Push (BinaryNotEqual (binary, left, right));
+ return;
+ case ExpressionType.LessThan:
+ Push (Comparer<object>.Default.Compare (left, right) < 0);
+ return;
+ case ExpressionType.LessThanOrEqual:
+ Push (Comparer<object>.Default.Compare (left, right) <= 0);
+ return;
+ case ExpressionType.GreaterThan:
+ Push (Comparer<object>.Default.Compare (left, right) > 0);
+ return;
+ case ExpressionType.GreaterThanOrEqual:
+ Push (Comparer<object>.Default.Compare (left, right) >= 0);
+ return;
+ }
+ }
+
+ void VisitLogicalShortCircuitBinary (BinaryExpression binary)
+ {
+ switch (binary.NodeType) {
+ case ExpressionType.AndAlso:
+ VisitAndAlso (binary);
+ return;
+ case ExpressionType.OrElse:
+ VisitOrElse (binary);
+ return;
+ }
+ }
+
+ void VisitArrayIndex (BinaryExpression binary)
+ {
+ Visit (binary.Left);
+ var left = Pop ();
+ Visit (binary.Right);
+ var right = Pop ();
+
+ Push (((Array) left).GetValue ((int) right));
+ }
+
+ bool IsNullBinaryLifting (BinaryExpression binary)
+ {
+ var right = Pop ();
+ var left = Pop ();
+
+ if (binary.IsLifted && (right == null || left == null)) {
+ if (binary.IsLiftedToNull)
+ Push (null);
+ else
+ Push (GetDefaultValue (binary.Type));
+
+ return true;
+ }
+
+ Push (left);
+ Push (right);
+
+ return false;
+ }
+
+ static object GetDefaultValue (Type type)
+ {
+ var array = (Array) Array.CreateInstance (type, 1);
+ return array.GetValue (0);
+ }
+
+ void VisitUserDefinedBinary (BinaryExpression binary)
+ {
+ switch (binary.NodeType) {
+ case ExpressionType.AndAlso:
+ case ExpressionType.OrElse:
+ VisitUserDefinedLogicalShortCircuitBinary (binary);
+ return;
+ case ExpressionType.Equal:
+ case ExpressionType.NotEqual:
+ VisitUserDefinedRelationalBinary (binary);
+ return;
+ default:
+ VisitUserDefinedCommonBinary (binary);
+ return;
+ }
+ }
+
+ void VisitUserDefinedLogicalShortCircuitBinary (BinaryExpression binary)
+ {
+ switch (binary.NodeType) {
+ case ExpressionType.AndAlso:
+ VisitUserDefinedAndAlso (binary);
+ return;
+ case ExpressionType.OrElse:
+ VisitUserDefinedOrElse (binary);
+ return;
+ }
+ }
+
+ void VisitUserDefinedRelationalBinary (BinaryExpression binary)
+ {
+ Visit (binary.Left);
+ Visit (binary.Right);
+
+ if (IsNullRelationalBinaryLifting (binary))
+ return;
+
+ var right = Pop ();
+ var left = Pop ();
+
+ Push (InvokeBinary (binary, left, right));
+ }
+
+ void VisitUserDefinedCommonBinary (BinaryExpression binary)
+ {
+ Visit (binary.Left);
+ Visit (binary.Right);
+
+ if (IsNullBinaryLifting (binary))
+ return;
+
+ var right = Pop ();
+ var left = Pop ();
+
+ Push (InvokeBinary (binary, left, right));
+ }
+
+ object InvokeBinary (BinaryExpression binary, object left, object right)
+ {
+ return InvokeMethod (binary.Method, null, new [] { left, right });
+ }
+
+ bool BinaryEqual (BinaryExpression binary, object left, object right)
+ {
+ if (typeof (ValueType).IsAssignableFrom (binary.Right.Type))
+ return ValueType.Equals (left, right);
+ else
+ return left == right;
+ }
+
+ bool BinaryNotEqual (BinaryExpression binary, object left, object right)
+ {
+ if (typeof (ValueType).IsAssignableFrom (binary.Right.Type))
+ return !ValueType.Equals (left, right);
+ else
+ return left != right;
+ }
+
+ protected override void VisitBinary (BinaryExpression binary)
+ {
+ if (binary.Method != null) {
+ VisitUserDefinedBinary (binary);
+ return;
+ }
+
+ switch (binary.NodeType) {
+ case ExpressionType.ArrayIndex:
+ VisitArrayIndex (binary);
+ return;
+ case ExpressionType.Coalesce:
+ VisitCoalesce (binary);
+ return;
+ case ExpressionType.AndAlso:
+ case ExpressionType.OrElse:
+ VisitLogicalShortCircuitBinary (binary);
+ return;
+ case ExpressionType.Equal:
+ case ExpressionType.NotEqual:
+ case ExpressionType.GreaterThan:
+ case ExpressionType.GreaterThanOrEqual:
+ case ExpressionType.LessThan:
+ case ExpressionType.LessThanOrEqual:
+ VisitRelationalBinary (binary);
+ return;
+ case ExpressionType.And:
+ case ExpressionType.Or:
+ VisitLogicalBinary (binary);
+ return;
+ case ExpressionType.Power:
+ case ExpressionType.Add:
+ case ExpressionType.AddChecked:
+ case ExpressionType.Divide:
+ case ExpressionType.ExclusiveOr:
+ case ExpressionType.LeftShift:
+ case ExpressionType.Modulo:
+ case ExpressionType.Multiply:
+ case ExpressionType.MultiplyChecked:
+ case ExpressionType.RightShift:
+ case ExpressionType.Subtract:
+ case ExpressionType.SubtractChecked:
+ VisitArithmeticBinary (binary);
+ return;
+ }
+ }
+
+ void VisitTypeAs (UnaryExpression unary)
+ {
+ Visit (unary.Operand);
+
+ var value = Pop ();
+ if (value == null || !Math.IsType (unary.Type, value))
+ Push (null);
+ else
+ Push (value);
+ }
+
+ void VisitArrayLength (UnaryExpression unary)
+ {
+ Visit (unary.Operand);
+
+ var array = (Array) Pop ();
+ Push (array.Length);
+ }
+
+ void VisitConvert (UnaryExpression unary)
+ {
+ if (unary.NodeType == ExpressionType.ConvertChecked)
+ VisitConvertChecked (unary);
+ else
+ VisitConvertUnchecked (unary);
+ }
+
+ void VisitConvertChecked (UnaryExpression unary)
+ {
+ VisitConvert (unary, Math.ConvertToTypeChecked);
+ }
+
+ void VisitConvertUnchecked (UnaryExpression unary)
+ {
+ VisitConvert (unary, Math.ConvertToTypeUnchecked);
+ }
+
+ void VisitConvert (UnaryExpression unary, Func<object, Type, Type, object> converter)
+ {
+ Visit (unary.Operand);
+ Push (converter (Pop (), unary.Operand.Type, unary.Type));
+ }
+
+ bool IsNullUnaryLifting (UnaryExpression unary)
+ {
+ var value = Pop ();
+
+ if (unary.IsLifted && value == null) {
+ if (unary.IsLiftedToNull) {
+ Push (null);
+ return true;
+ } else {
+ throw new InvalidOperationException ();
+ }
+ }
+
+ Push (value);
+ return false;
+ }
+
+ void VisitQuote (UnaryExpression unary)
+ {
+ Push (unary.Operand);
+ }
+
+ void VisitUserDefinedUnary (UnaryExpression unary)
+ {
+ Visit (unary.Operand);
+
+ if (IsNullUnaryLifting (unary))
+ return;
+
+ var value = Pop ();
+
+ Push (InvokeUnary (unary, value));
+ }
+
+ object InvokeUnary (UnaryExpression unary, object value)
+ {
+ return InvokeMethod (unary.Method, null, new [] { value });
+ }
+
+ void VisitArithmeticUnary (UnaryExpression unary)
+ {
+ Visit (unary.Operand);
+
+ if (IsNullUnaryLifting (unary))
+ return;
+
+ var value = Pop ();
+
+ switch (unary.NodeType) {
+ case ExpressionType.Not:
+ if (unary.Type.GetNotNullableType () == typeof (bool))
+ Push (!Convert.ToBoolean (value));
+ else
+ Push (~Convert.ToInt32 (value));
+ return;
+ case ExpressionType.Negate:
+ Push (Math.Negate (value, Type.GetTypeCode (unary.Type.GetNotNullableType ())));
+ return;
+ case ExpressionType.NegateChecked:
+ Push (Math.NegateChecked (value, Type.GetTypeCode (unary.Type.GetNotNullableType ())));
+ return;
+ case ExpressionType.UnaryPlus:
+ Push (value);
+ return;
+ }
+ }
+
+ protected override void VisitUnary (UnaryExpression unary)
+ {
+ if (unary.Method != null) {
+ VisitUserDefinedUnary (unary);
+ return;
+ }
+
+ switch (unary.NodeType) {
+ case ExpressionType.Quote:
+ VisitQuote (unary);
+ return;
+ case ExpressionType.TypeAs:
+ VisitTypeAs (unary);
+ return;
+ case ExpressionType.ArrayLength:
+ VisitArrayLength (unary);
+ return;
+ case ExpressionType.Convert:
+ case ExpressionType.ConvertChecked:
+ VisitConvert (unary);
+ return;
+ case ExpressionType.Negate:
+ case ExpressionType.NegateChecked:
+ case ExpressionType.Not:
+ case ExpressionType.UnaryPlus:
+ VisitArithmeticUnary (unary);
+ return;
+ default:
+ throw new NotImplementedException (unary.NodeType.ToString ());
+ }
+ }
+
+ protected override void VisitNew (NewExpression nex)
+ {
+ if (nex.Constructor == null)
+ Push (Activator.CreateInstance (nex.Type));
+ else
+ Push (InvokeConstructor (nex.Constructor, VisitListExpressions (nex.Arguments)));
+ }
+
+ static object InvokeConstructor (ConstructorInfo constructor, object [] arguments)
+ {
+ try {
+ return constructor.Invoke (arguments);
+ } catch (TargetInvocationException e) {
+ throw e.InnerException;
+ }
+ }
+
+ protected override void VisitTypeIs (TypeBinaryExpression type)
+ {
+ Visit (type.Expression);
+ Push (Math.IsType (type.TypeOperand, Pop ()));
+ }
+
+ void VisitMemberInfo (MemberInfo mi)
+ {
+ mi.OnFieldOrProperty (
+ field => {
+ object target = null;
+ if (!field.IsStatic)
+ target = Pop ();
+
+ Push (field.GetValue (target));
+ },
+ property => {
+ object target = null;
+ var getter = property.GetGetMethod (true);
+ if (!getter.IsStatic)
+ target = Pop ();
+
+ Push (property.GetValue (target, null));
+ });
+ }
+
+ protected override void VisitMemberAccess (MemberExpression member)
+ {
+ Visit (member.Expression);
+ VisitMemberInfo (member.Member);
+ }
+
+ protected override void VisitNewArray (NewArrayExpression newArray)
+ {
+ switch (newArray.NodeType) {
+ case ExpressionType.NewArrayInit:
+ VisitNewArrayInit (newArray);
+ return;
+ case ExpressionType.NewArrayBounds:
+ VisitNewArrayBounds (newArray);
+ return;
+ }
+
+ throw new NotSupportedException ();
+ }
+
+ void VisitNewArrayBounds (NewArrayExpression newArray)
+ {
+ var lengths = new int [newArray.Expressions.Count];
+ for (int i = 0; i < lengths.Length; i++) {
+ Visit (newArray.Expressions [i]);
+ lengths [i] = (int) Pop ();
+ }
+
+ Push (Array.CreateInstance (newArray.Type.GetElementType (), lengths));
+ }
+
+ void VisitNewArrayInit (NewArrayExpression newArray)
+ {
+ var array = Array.CreateInstance (
+ newArray.Type.GetElementType (),
+ newArray.Expressions.Count);
+
+ for (int i = 0; i < array.Length; i++) {
+ Visit (newArray.Expressions [i]);
+ array.SetValue (Pop (), i);
+ }
+
+ Push (array);
+ }
+
+ protected override void VisitConditional (ConditionalExpression conditional)
+ {
+ Visit (conditional.Test);
+
+ if ((bool) Pop ())
+ Visit (conditional.IfTrue);
+ else
+ Visit (conditional.IfFalse);
+ }
+
+ protected override void VisitMethodCall (MethodCallExpression call)
+ {
+ object instance = null;
+ if (call.Object != null) {
+ Visit (call.Object);
+ instance = Pop ();
+ }
+
+ Push (InvokeMethod (call.Method, instance, VisitListExpressions (call.Arguments)));
+ }
+
+ protected override void VisitParameter (ParameterExpression parameter)
+ {
+ var info = lambdas.Peek ();
+
+ var lambda = info.Lambda;
+ var arguments = info.Arguments;
+
+ var index = GetParameterIndex (lambda, parameter);
+ if (index >= 0) {
+ Push (arguments [index]);
+ return;
+ }
+
+ object value;
+ if (hoisted_values.TryGetValue (parameter, out value)) {
+ Push (value);
+ return;
+ }
+
+ throw new ArgumentException ();
+ }
+
+ protected override void VisitConstant (ConstantExpression constant)
+ {
+ Push (constant.Value);
+ }
+
+ protected override void VisitInvocation (InvocationExpression invocation)
+ {
+ Visit (invocation.Expression);
+ Push (Invoke ((Delegate) Pop (), VisitListExpressions (invocation.Arguments)));
+ }
+
+ static object Invoke (Delegate dlg, object [] arguments)
+ {
+ return InvokeMethod (dlg.Method, dlg.Target, arguments);
+ }
+
+ static object InvokeMethod (MethodBase method, object obj, object [] arguments)
+ {
+ try {
+ return method.Invoke (obj, arguments);
+ } catch (TargetInvocationException e) {
+ throw e.InnerException;
+ }
+ }
+
+ protected override void VisitMemberListBinding (MemberListBinding binding)
+ {
+ var value = Pop ();
+ Push (value);
+ VisitMemberInfo (binding.Member);
+ VisitElementInitializerList (binding.Initializers);
+ Pop (); // pop the member
+ Push (value); // push the original target
+ }
+
+ protected override void VisitElementInitializer (ElementInit initializer)
+ {
+ object target = null;
+ if (!initializer.AddMethod.IsStatic)
+ target = Pop ();
+
+ var arguments = VisitListExpressions (initializer.Arguments);
+ InvokeMethod (initializer.AddMethod, target, arguments);
+
+ if (!initializer.AddMethod.IsStatic)
+ Push (target);
+ }
+
+ protected override void VisitMemberMemberBinding (MemberMemberBinding binding)
+ {
+ var value = Pop ();
+ Push (value);
+ VisitMemberInfo (binding.Member);
+ VisitBindingList (binding.Bindings);
+ Pop ();
+ Push (value);
+ }
+
+ protected override void VisitMemberAssignment (MemberAssignment assignment)
+ {
+ Visit (assignment.Expression);
+
+ var value = Pop ();
+
+ assignment.Member.OnFieldOrProperty (
+ field => {
+ object target = null;
+ if (!field.IsStatic)
+ target = Pop ();
+
+ field.SetValue (target, value);
+
+ if (!field.IsStatic)
+ Push (target);
+ },
+ property => {
+ object target = null;
+ var getter = property.GetGetMethod (true);
+ if (!getter.IsStatic)
+ target = Pop ();
+
+ property.SetValue (target, value, null);
+
+ if (!getter.IsStatic)
+ Push (target);
+ });
+ }
+
+ protected override void VisitLambda (LambdaExpression lambda)
+ {
+ Push (lambda.Compile (this));
+ }
+
+ private object [] VisitListExpressions (ReadOnlyCollection<Expression> collection)
+ {
+ object [] results = new object [collection.Count];
+ for (int i = 0; i < results.Length; i++) {
+ Visit (collection [i]);
+ results [i] = Pop ();
+ }
+
+ return results;
+ }
+
+ void StoreHoistedVariables (LambdaExpression lambda, object [] arguments)
+ {
+ if (hoisted_map == null)
+ return;
+
+ List<ParameterExpression> variables;
+ if (!hoisted_map.TryGetValue (lambda, out variables))
+ return;
+
+ foreach (var variable in variables)
+ StoreHoistedVariable (variable, lambda, arguments);
+ }
+
+ void StoreHoistedVariable (ParameterExpression variable, LambdaExpression lambda, object [] arguments)
+ {
+ var index = GetParameterIndex (lambda, variable);
+ if (index < 0)
+ return;
+
+ hoisted_values [variable] = arguments [index];
+ }
+
+ static int GetParameterIndex (LambdaExpression lambda, ParameterExpression parameter)
+ {
+ return lambda.Parameters.IndexOf (parameter);
+ }
+
+ public object Interpret (LambdaExpression lambda, object [] arguments)
+ {
+ lambdas.Push (new LambdaInfo (lambda, arguments));
+
+ StoreHoistedVariables (lambda, arguments);
+
+ Visit (lambda.Body);
+
+ lambdas.Pop ();
+
+ if (lambda.GetReturnType () != typeof (void))
+ return Pop ();
+
+ return null;
+ }
+ }
+}