2 // ExpressionInterpreter.cs
4 // (C) 2008 Mainsoft, Inc. (http://www.mainsoft.com)
5 // (C) 2008 db4objects, Inc. (http://www.db4o.com)
6 // (C) 2010 Novell, Inc. (http://www.novell.com)
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System.Collections.Generic;
29 using System.Collections.ObjectModel;
30 using System.Linq.Expressions;
31 using System.Reflection;
33 namespace System.Linq.jvm {
36 public readonly LambdaExpression Lambda;
37 public readonly object [] Arguments;
39 public LambdaInfo (LambdaExpression lambda, object [] arguments)
42 this.Arguments = arguments;
46 class HoistedVariableDetector : ExpressionVisitor {
48 readonly Dictionary<ParameterExpression, LambdaExpression> parameter_to_lambda =
49 new Dictionary<ParameterExpression, LambdaExpression> ();
51 Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
53 LambdaExpression lambda;
55 public Dictionary<LambdaExpression, List<ParameterExpression>> Process (LambdaExpression lambda)
61 protected override void VisitLambda (LambdaExpression lambda)
64 foreach (var parameter in lambda.Parameters)
65 parameter_to_lambda [parameter] = lambda;
66 base.VisitLambda (lambda);
69 protected override void VisitParameter (ParameterExpression parameter)
71 if (lambda.Parameters.Contains (parameter))
77 void Hoist (ParameterExpression parameter)
79 LambdaExpression lambda;
80 if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
83 if (hoisted_map == null)
84 hoisted_map = new Dictionary<LambdaExpression, List<ParameterExpression>> ();
86 List<ParameterExpression> hoisted;
87 if (!hoisted_map.TryGetValue (lambda, out hoisted)) {
88 hoisted = new List<ParameterExpression> ();
89 hoisted_map [lambda] = hoisted;
92 hoisted.Add (parameter);
97 class ExpressionInterpreter : ExpressionVisitor {
99 readonly Stack<LambdaInfo> lambdas = new Stack<LambdaInfo> ();
100 readonly Stack<object> stack = new Stack<object> ();
102 readonly Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
103 readonly Dictionary<ParameterExpression, object> hoisted_values;
105 void Push (object value)
115 public ExpressionInterpreter (LambdaExpression lambda)
117 hoisted_map = new HoistedVariableDetector ().Process (lambda);
119 if (hoisted_map != null)
120 hoisted_values = new Dictionary<ParameterExpression, object> ();
123 private void VisitCoalesce (BinaryExpression binary)
130 Visit (binary.Right);
134 if (binary.Conversion == null) {
139 Push (Invoke (binary.Conversion.Compile (this), new [] { left }));
142 void VisitAndAlso (BinaryExpression binary)
151 if (left == null || ((bool) left)) {
152 Visit (binary.Right);
156 Push (Math.And (left, right));
159 void VisitUserDefinedAndAlso (BinaryExpression binary)
168 if (InvokeFalseOperator (binary, left)) {
173 Visit (binary.Right);
176 if (binary.IsLiftedToNull && right == null) {
181 Push (InvokeMethod (binary.Method, null, new [] { left, right }));
184 static bool InvokeTrueOperator (BinaryExpression binary, object target)
186 return (bool) InvokeMethod (GetTrueOperator (binary), null, new [] { target });
189 static bool InvokeFalseOperator (BinaryExpression binary, object target)
191 return (bool) InvokeMethod (GetFalseOperator (binary), null, new [] { target });
194 static MethodInfo GetFalseOperator (BinaryExpression binary)
196 return Expression.GetFalseOperator (binary.Left.Type.GetNotNullableType ());
199 static MethodInfo GetTrueOperator (BinaryExpression binary)
201 return Expression.GetTrueOperator (binary.Left.Type.GetNotNullableType ());
204 void VisitOrElse (BinaryExpression binary)
212 if (left == null || !((bool) left)) {
213 Visit (binary.Right);
217 Push (Math.Or (left, right));
220 void VisitUserDefinedOrElse (BinaryExpression binary)
228 if (InvokeTrueOperator (binary, left)) {
233 Visit (binary.Right);
236 if (binary.IsLiftedToNull && right == null) {
241 Push (InvokeMethod (binary.Method, null, new [] { left, right }));
244 void VisitLogicalBinary (BinaryExpression binary)
247 Visit (binary.Right);
252 Push (Math.Evaluate (left, right, binary.Type, binary.NodeType));
255 void VisitArithmeticBinary (BinaryExpression binary)
258 Visit (binary.Right);
260 if (IsNullBinaryLifting (binary))
266 switch (binary.NodeType) {
267 case ExpressionType.RightShift:
268 Push (Math.RightShift (left, Convert.ToInt32 (right), Type.GetTypeCode (binary.Type.GetNotNullableType ())));
270 case ExpressionType.LeftShift:
271 Push (Math.LeftShift (left, Convert.ToInt32 (right), Type.GetTypeCode (binary.Type.GetNotNullableType ())));
274 Push (Math.Evaluate (left, right, binary.Type, binary.NodeType));
279 bool IsNullRelationalBinaryLifting (BinaryExpression binary)
284 if (binary.IsLifted && (left == null || right == null)) {
285 if (binary.IsLiftedToNull) {
290 switch (binary.NodeType) {
291 case ExpressionType.Equal:
292 Push (BinaryEqual (binary, left, right));
294 case ExpressionType.NotEqual:
295 Push (BinaryNotEqual (binary, left, right));
311 void VisitRelationalBinary (BinaryExpression binary)
314 Visit (binary.Right);
316 if (IsNullRelationalBinaryLifting (binary))
322 switch (binary.NodeType) {
323 case ExpressionType.Equal:
324 Push (BinaryEqual (binary, left, right));
326 case ExpressionType.NotEqual:
327 Push (BinaryNotEqual (binary, left, right));
329 case ExpressionType.LessThan:
330 Push (Comparer<object>.Default.Compare (left, right) < 0);
332 case ExpressionType.LessThanOrEqual:
333 Push (Comparer<object>.Default.Compare (left, right) <= 0);
335 case ExpressionType.GreaterThan:
336 Push (Comparer<object>.Default.Compare (left, right) > 0);
338 case ExpressionType.GreaterThanOrEqual:
339 Push (Comparer<object>.Default.Compare (left, right) >= 0);
344 void VisitLogicalShortCircuitBinary (BinaryExpression binary)
346 switch (binary.NodeType) {
347 case ExpressionType.AndAlso:
348 VisitAndAlso (binary);
350 case ExpressionType.OrElse:
351 VisitOrElse (binary);
356 void VisitArrayIndex (BinaryExpression binary)
360 Visit (binary.Right);
363 Push (((Array) left).GetValue ((int) right));
366 bool IsNullBinaryLifting (BinaryExpression binary)
371 if (binary.IsLifted && (right == null || left == null)) {
372 if (binary.IsLiftedToNull)
375 Push (GetDefaultValue (binary.Type));
386 static object GetDefaultValue (Type type)
388 var array = (Array) Array.CreateInstance (type, 1);
389 return array.GetValue (0);
392 void VisitUserDefinedBinary (BinaryExpression binary)
394 switch (binary.NodeType) {
395 case ExpressionType.AndAlso:
396 case ExpressionType.OrElse:
397 VisitUserDefinedLogicalShortCircuitBinary (binary);
399 case ExpressionType.Equal:
400 case ExpressionType.NotEqual:
401 VisitUserDefinedRelationalBinary (binary);
404 VisitUserDefinedCommonBinary (binary);
409 void VisitUserDefinedLogicalShortCircuitBinary (BinaryExpression binary)
411 switch (binary.NodeType) {
412 case ExpressionType.AndAlso:
413 VisitUserDefinedAndAlso (binary);
415 case ExpressionType.OrElse:
416 VisitUserDefinedOrElse (binary);
421 void VisitUserDefinedRelationalBinary (BinaryExpression binary)
424 Visit (binary.Right);
426 if (IsNullRelationalBinaryLifting (binary))
432 Push (InvokeBinary (binary, left, right));
435 void VisitUserDefinedCommonBinary (BinaryExpression binary)
438 Visit (binary.Right);
440 if (IsNullBinaryLifting (binary))
446 Push (InvokeBinary (binary, left, right));
449 object InvokeBinary (BinaryExpression binary, object left, object right)
451 return InvokeMethod (binary.Method, null, new [] { left, right });
454 bool BinaryEqual (BinaryExpression binary, object left, object right)
456 if (typeof (ValueType).IsAssignableFrom (binary.Right.Type))
457 return ValueType.Equals (left, right);
459 return left == right;
462 bool BinaryNotEqual (BinaryExpression binary, object left, object right)
464 if (typeof (ValueType).IsAssignableFrom (binary.Right.Type))
465 return !ValueType.Equals (left, right);
467 return left != right;
470 protected override void VisitBinary (BinaryExpression binary)
472 if (binary.Method != null) {
473 VisitUserDefinedBinary (binary);
477 switch (binary.NodeType) {
478 case ExpressionType.ArrayIndex:
479 VisitArrayIndex (binary);
481 case ExpressionType.Coalesce:
482 VisitCoalesce (binary);
484 case ExpressionType.AndAlso:
485 case ExpressionType.OrElse:
486 VisitLogicalShortCircuitBinary (binary);
488 case ExpressionType.Equal:
489 case ExpressionType.NotEqual:
490 case ExpressionType.GreaterThan:
491 case ExpressionType.GreaterThanOrEqual:
492 case ExpressionType.LessThan:
493 case ExpressionType.LessThanOrEqual:
494 VisitRelationalBinary (binary);
496 case ExpressionType.And:
497 case ExpressionType.Or:
498 VisitLogicalBinary (binary);
500 case ExpressionType.Power:
501 case ExpressionType.Add:
502 case ExpressionType.AddChecked:
503 case ExpressionType.Divide:
504 case ExpressionType.ExclusiveOr:
505 case ExpressionType.LeftShift:
506 case ExpressionType.Modulo:
507 case ExpressionType.Multiply:
508 case ExpressionType.MultiplyChecked:
509 case ExpressionType.RightShift:
510 case ExpressionType.Subtract:
511 case ExpressionType.SubtractChecked:
512 VisitArithmeticBinary (binary);
517 void VisitTypeAs (UnaryExpression unary)
519 Visit (unary.Operand);
522 if (value == null || !Math.IsType (unary.Type, value))
528 void VisitArrayLength (UnaryExpression unary)
530 Visit (unary.Operand);
532 var array = (Array) Pop ();
536 void VisitConvert (UnaryExpression unary)
538 if (unary.NodeType == ExpressionType.ConvertChecked)
539 VisitConvertChecked (unary);
541 VisitConvertUnchecked (unary);
544 void VisitConvertChecked (UnaryExpression unary)
546 VisitConvert (unary, Math.ConvertToTypeChecked);
549 void VisitConvertUnchecked (UnaryExpression unary)
551 VisitConvert (unary, Math.ConvertToTypeUnchecked);
554 void VisitConvert (UnaryExpression unary, Func<object, Type, Type, object> converter)
556 Visit (unary.Operand);
557 Push (converter (Pop (), unary.Operand.Type, unary.Type));
560 bool IsNullUnaryLifting (UnaryExpression unary)
564 if (unary.IsLifted && value == null) {
565 if (unary.IsLiftedToNull) {
569 throw new InvalidOperationException ();
577 void VisitQuote (UnaryExpression unary)
579 Push (unary.Operand);
582 void VisitUserDefinedUnary (UnaryExpression unary)
584 Visit (unary.Operand);
586 if (IsNullUnaryLifting (unary))
591 Push (InvokeUnary (unary, value));
594 object InvokeUnary (UnaryExpression unary, object value)
596 return InvokeMethod (unary.Method, null, new [] { value });
599 void VisitArithmeticUnary (UnaryExpression unary)
601 Visit (unary.Operand);
603 if (IsNullUnaryLifting (unary))
608 switch (unary.NodeType) {
609 case ExpressionType.Not:
610 if (unary.Type.GetNotNullableType () == typeof (bool))
611 Push (!Convert.ToBoolean (value));
613 Push (~Convert.ToInt32 (value));
615 case ExpressionType.Negate:
616 Push (Math.Negate (value, Type.GetTypeCode (unary.Type.GetNotNullableType ())));
618 case ExpressionType.NegateChecked:
619 Push (Math.NegateChecked (value, Type.GetTypeCode (unary.Type.GetNotNullableType ())));
621 case ExpressionType.UnaryPlus:
627 protected override void VisitUnary (UnaryExpression unary)
629 if (unary.Method != null) {
630 VisitUserDefinedUnary (unary);
634 switch (unary.NodeType) {
635 case ExpressionType.Quote:
638 case ExpressionType.TypeAs:
641 case ExpressionType.ArrayLength:
642 VisitArrayLength (unary);
644 case ExpressionType.Convert:
645 case ExpressionType.ConvertChecked:
646 VisitConvert (unary);
648 case ExpressionType.Negate:
649 case ExpressionType.NegateChecked:
650 case ExpressionType.Not:
651 case ExpressionType.UnaryPlus:
652 VisitArithmeticUnary (unary);
655 throw new NotImplementedException (unary.NodeType.ToString ());
659 protected override void VisitNew (NewExpression nex)
661 if (nex.Constructor == null)
662 Push (Activator.CreateInstance (nex.Type));
664 Push (InvokeConstructor (nex.Constructor, VisitListExpressions (nex.Arguments)));
667 static object InvokeConstructor (ConstructorInfo constructor, object [] arguments)
670 return constructor.Invoke (arguments);
671 } catch (TargetInvocationException e) {
672 throw e.InnerException;
676 protected override void VisitTypeIs (TypeBinaryExpression type)
678 Visit (type.Expression);
679 Push (Math.IsType (type.TypeOperand, Pop ()));
682 void VisitMemberInfo (MemberInfo mi)
684 mi.OnFieldOrProperty (
686 object target = null;
690 Push (field.GetValue (target));
693 object target = null;
694 var getter = property.GetGetMethod (true);
695 if (!getter.IsStatic)
698 Push (property.GetValue (target, null));
702 protected override void VisitMemberAccess (MemberExpression member)
704 Visit (member.Expression);
705 VisitMemberInfo (member.Member);
708 protected override void VisitNewArray (NewArrayExpression newArray)
710 switch (newArray.NodeType) {
711 case ExpressionType.NewArrayInit:
712 VisitNewArrayInit (newArray);
714 case ExpressionType.NewArrayBounds:
715 VisitNewArrayBounds (newArray);
719 throw new NotSupportedException ();
722 void VisitNewArrayBounds (NewArrayExpression newArray)
724 var lengths = new int [newArray.Expressions.Count];
725 for (int i = 0; i < lengths.Length; i++) {
726 Visit (newArray.Expressions [i]);
727 lengths [i] = (int) Pop ();
730 Push (Array.CreateInstance (newArray.Type.GetElementType (), lengths));
733 void VisitNewArrayInit (NewArrayExpression newArray)
735 var array = Array.CreateInstance (
736 newArray.Type.GetElementType (),
737 newArray.Expressions.Count);
739 for (int i = 0; i < array.Length; i++) {
740 Visit (newArray.Expressions [i]);
741 array.SetValue (Pop (), i);
747 protected override void VisitConditional (ConditionalExpression conditional)
749 Visit (conditional.Test);
752 Visit (conditional.IfTrue);
754 Visit (conditional.IfFalse);
757 protected override void VisitMethodCall (MethodCallExpression call)
759 object instance = null;
760 if (call.Object != null) {
765 Push (InvokeMethod (call.Method, instance, VisitListExpressions (call.Arguments)));
768 protected override void VisitParameter (ParameterExpression parameter)
770 var info = lambdas.Peek ();
772 var lambda = info.Lambda;
773 var arguments = info.Arguments;
775 var index = GetParameterIndex (lambda, parameter);
777 Push (arguments [index]);
782 if (hoisted_values.TryGetValue (parameter, out value)) {
787 throw new ArgumentException ();
790 protected override void VisitConstant (ConstantExpression constant)
792 Push (constant.Value);
795 protected override void VisitInvocation (InvocationExpression invocation)
797 Visit (invocation.Expression);
798 Push (Invoke ((Delegate) Pop (), VisitListExpressions (invocation.Arguments)));
801 static object Invoke (Delegate dlg, object [] arguments)
803 return InvokeMethod (dlg.Method, dlg.Target, arguments);
806 static object InvokeMethod (MethodBase method, object obj, object [] arguments)
809 return method.Invoke (obj, arguments);
810 } catch (TargetInvocationException e) {
811 throw e.InnerException;
815 protected override void VisitMemberListBinding (MemberListBinding binding)
819 VisitMemberInfo (binding.Member);
820 VisitElementInitializerList (binding.Initializers);
821 Pop (); // pop the member
822 Push (value); // push the original target
825 protected override void VisitElementInitializer (ElementInit initializer)
827 object target = null;
828 if (!initializer.AddMethod.IsStatic)
831 var arguments = VisitListExpressions (initializer.Arguments);
832 InvokeMethod (initializer.AddMethod, target, arguments);
834 if (!initializer.AddMethod.IsStatic)
838 protected override void VisitMemberMemberBinding (MemberMemberBinding binding)
842 VisitMemberInfo (binding.Member);
843 VisitBindingList (binding.Bindings);
848 protected override void VisitMemberAssignment (MemberAssignment assignment)
850 Visit (assignment.Expression);
854 assignment.Member.OnFieldOrProperty (
856 object target = null;
860 field.SetValue (target, value);
866 object target = null;
867 var getter = property.GetGetMethod (true);
868 if (!getter.IsStatic)
871 property.SetValue (target, value, null);
873 if (!getter.IsStatic)
878 protected override void VisitLambda (LambdaExpression lambda)
880 Push (lambda.Compile (this));
883 private object [] VisitListExpressions (ReadOnlyCollection<Expression> collection)
885 object [] results = new object [collection.Count];
886 for (int i = 0; i < results.Length; i++) {
887 Visit (collection [i]);
888 results [i] = Pop ();
894 void StoreHoistedVariables (LambdaExpression lambda, object [] arguments)
896 if (hoisted_map == null)
899 List<ParameterExpression> variables;
900 if (!hoisted_map.TryGetValue (lambda, out variables))
903 foreach (var variable in variables)
904 StoreHoistedVariable (variable, lambda, arguments);
907 void StoreHoistedVariable (ParameterExpression variable, LambdaExpression lambda, object [] arguments)
909 var index = GetParameterIndex (lambda, variable);
913 hoisted_values [variable] = arguments [index];
916 static int GetParameterIndex (LambdaExpression lambda, ParameterExpression parameter)
918 return lambda.Parameters.IndexOf (parameter);
921 public object Interpret (LambdaExpression lambda, object [] arguments)
923 lambdas.Push (new LambdaInfo (lambda, arguments));
925 StoreHoistedVariables (lambda, arguments);
931 if (lambda.GetReturnType () != typeof (void))