2 // ExpressionInterpreter.cs
4 // (C) 2008 Mainsoft, Inc. (http://www.mainsoft.com)
5 // (C) 2008 db4objects, Inc. (http://www.db4o.com)
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System
.Collections
;
29 using System
.Collections
.Generic
;
30 using System
.Collections
.ObjectModel
;
31 using System
.Linq
.Expressions
;
32 using System
.Reflection
;
34 namespace System
.Linq
.jvm
{
36 class ExpressionInterpreter
: ExpressionVisitor
{
38 LambdaExpression lambda
;
40 Stack
<object> stack
= new Stack
<object> ();
42 void Push (object value)
52 public ExpressionInterpreter (LambdaExpression lambda
, object [] arguments
)
55 this.arguments
= arguments
;
58 private void VisitCoalesce (BinaryExpression binary
)
69 if (binary
.Conversion
== null) {
74 Push (Invoke (binary
.Conversion
.Compile (), new [] { left }
));
77 void VisitAndAlso (BinaryExpression binary
)
86 if (left
== null || ((bool) left
)) {
91 Push (Math
.And (left
, right
));
94 void VisitUserDefinedAndAlso (BinaryExpression binary
)
103 if (InvokeFalseOperator (binary
, left
)) {
108 Visit (binary
.Right
);
111 if (binary
.IsLiftedToNull
&& right
== null) {
116 Push (InvokeMethod (binary
.Method
, null, new [] { left, right }
));
119 static bool InvokeTrueOperator (BinaryExpression binary
, object target
)
121 return (bool) InvokeMethod (GetTrueOperator (binary
), null, new [] { target }
);
124 static bool InvokeFalseOperator (BinaryExpression binary
, object target
)
126 return (bool) InvokeMethod (GetFalseOperator (binary
), null, new [] { target }
);
129 static MethodInfo
GetFalseOperator (BinaryExpression binary
)
131 return Expression
.GetFalseOperator (binary
.Left
.Type
.GetNotNullableType ());
134 static MethodInfo
GetTrueOperator (BinaryExpression binary
)
136 return Expression
.GetTrueOperator (binary
.Left
.Type
.GetNotNullableType ());
139 void VisitOrElse (BinaryExpression binary
)
147 if (left
== null || !((bool) left
)) {
148 Visit (binary
.Right
);
152 Push (Math
.Or (left
, right
));
155 void VisitUserDefinedOrElse (BinaryExpression binary
)
163 if (InvokeTrueOperator (binary
, left
)) {
168 Visit (binary
.Right
);
171 if (binary
.IsLiftedToNull
&& right
== null) {
176 Push (InvokeMethod (binary
.Method
, null, new [] { left, right }
));
179 void VisitLogicalBinary (BinaryExpression binary
)
182 Visit (binary
.Right
);
187 Push (Math
.Evaluate (left
, right
, binary
.Type
, binary
.NodeType
));
190 void VisitArithmeticBinary (BinaryExpression binary
)
193 Visit (binary
.Right
);
195 if (IsNullBinaryLifting (binary
))
201 switch (binary
.NodeType
) {
202 case ExpressionType
.RightShift
:
203 Push (Math
.RightShift (left
, Convert
.ToInt32 (right
), Type
.GetTypeCode (binary
.Type
.GetNotNullableType ())));
205 case ExpressionType
.LeftShift
:
206 Push (Math
.LeftShift (left
, Convert
.ToInt32 (right
), Type
.GetTypeCode (binary
.Type
.GetNotNullableType ())));
209 Push (Math
.Evaluate (left
, right
, binary
.Type
, binary
.NodeType
));
214 bool IsNullRelationalBinaryLifting (BinaryExpression binary
)
219 if (binary
.IsLifted
&& (left
== null || right
== null)) {
220 if (binary
.IsLiftedToNull
) {
225 switch (binary
.NodeType
) {
226 case ExpressionType
.Equal
:
227 Push (BinaryEqual (binary
, left
, right
));
229 case ExpressionType
.NotEqual
:
230 Push (BinaryNotEqual (binary
, left
, right
));
246 void VisitRelationalBinary (BinaryExpression binary
)
249 Visit (binary
.Right
);
251 if (IsNullRelationalBinaryLifting (binary
))
257 switch (binary
.NodeType
) {
258 case ExpressionType
.Equal
:
259 Push (BinaryEqual (binary
, left
, right
));
261 case ExpressionType
.NotEqual
:
262 Push (BinaryNotEqual (binary
, left
, right
));
264 case ExpressionType
.LessThan
:
265 Push (Comparer
.Default
.Compare (left
, right
) < 0);
267 case ExpressionType
.LessThanOrEqual
:
268 Push (Comparer
.Default
.Compare (left
, right
) <= 0);
270 case ExpressionType
.GreaterThan
:
271 Push (Comparer
.Default
.Compare (left
, right
) > 0);
273 case ExpressionType
.GreaterThanOrEqual
:
274 Push (Comparer
.Default
.Compare (left
, right
) >= 0);
279 void VisitLogicalShortCircuitBinary (BinaryExpression binary
)
281 switch (binary
.NodeType
) {
282 case ExpressionType
.AndAlso
:
283 VisitAndAlso (binary
);
285 case ExpressionType
.OrElse
:
286 VisitOrElse (binary
);
291 void VisitArrayIndex (BinaryExpression binary
)
295 Visit (binary
.Right
);
298 Push (((Array
) left
).GetValue ((int) right
));
301 bool IsNullBinaryLifting (BinaryExpression binary
)
306 if (binary
.IsLifted
&& (right
== null || left
== null)) {
307 if (binary
.IsLiftedToNull
)
310 Push (GetDefaultValue (binary
.Type
));
321 static object GetDefaultValue (Type type
)
323 var array
= (Array
) Array
.CreateInstance (type
, 1);
324 return array
.GetValue (0);
327 void VisitUserDefinedBinary (BinaryExpression binary
)
329 switch (binary
.NodeType
) {
330 case ExpressionType
.AndAlso
:
331 case ExpressionType
.OrElse
:
332 VisitUserDefinedLogicalShortCircuitBinary (binary
);
334 case ExpressionType
.Equal
:
335 case ExpressionType
.NotEqual
:
336 VisitUserDefinedRelationalBinary (binary
);
339 VisitUserDefinedCommonBinary (binary
);
344 void VisitUserDefinedLogicalShortCircuitBinary (BinaryExpression binary
)
346 switch (binary
.NodeType
) {
347 case ExpressionType
.AndAlso
:
348 VisitUserDefinedAndAlso (binary
);
350 case ExpressionType
.OrElse
:
351 VisitUserDefinedOrElse (binary
);
356 void VisitUserDefinedRelationalBinary (BinaryExpression binary
)
359 Visit (binary
.Right
);
361 if (IsNullRelationalBinaryLifting (binary
))
367 Push (InvokeBinary (binary
, left
, right
));
370 void VisitUserDefinedCommonBinary (BinaryExpression binary
)
373 Visit (binary
.Right
);
375 if (IsNullBinaryLifting (binary
))
381 Push (InvokeBinary (binary
, left
, right
));
384 object InvokeBinary (BinaryExpression binary
, object left
, object right
)
386 return InvokeMethod (binary
.Method
, null, new [] { left, right }
);
389 bool BinaryEqual (BinaryExpression binary
, object left
, object right
)
391 if (typeof (ValueType
).IsAssignableFrom (binary
.Right
.Type
))
392 return ValueType
.Equals (left
, right
);
394 return left
== right
;
397 bool BinaryNotEqual (BinaryExpression binary
, object left
, object right
)
399 if (typeof (ValueType
).IsAssignableFrom (binary
.Right
.Type
))
400 return !ValueType
.Equals (left
, right
);
402 return left
!= right
;
405 protected override void VisitBinary (BinaryExpression binary
)
407 if (binary
.Method
!= null) {
408 VisitUserDefinedBinary (binary
);
412 switch (binary
.NodeType
) {
413 case ExpressionType
.ArrayIndex
:
414 VisitArrayIndex (binary
);
416 case ExpressionType
.Coalesce
:
417 VisitCoalesce (binary
);
419 case ExpressionType
.AndAlso
:
420 case ExpressionType
.OrElse
:
421 VisitLogicalShortCircuitBinary (binary
);
423 case ExpressionType
.Equal
:
424 case ExpressionType
.NotEqual
:
425 case ExpressionType
.GreaterThan
:
426 case ExpressionType
.GreaterThanOrEqual
:
427 case ExpressionType
.LessThan
:
428 case ExpressionType
.LessThanOrEqual
:
429 VisitRelationalBinary (binary
);
431 case ExpressionType
.And
:
432 case ExpressionType
.Or
:
433 VisitLogicalBinary (binary
);
435 case ExpressionType
.Power
:
436 case ExpressionType
.Add
:
437 case ExpressionType
.AddChecked
:
438 case ExpressionType
.Divide
:
439 case ExpressionType
.ExclusiveOr
:
440 case ExpressionType
.LeftShift
:
441 case ExpressionType
.Modulo
:
442 case ExpressionType
.Multiply
:
443 case ExpressionType
.MultiplyChecked
:
444 case ExpressionType
.RightShift
:
445 case ExpressionType
.Subtract
:
446 case ExpressionType
.SubtractChecked
:
447 VisitArithmeticBinary (binary
);
452 void VisitTypeAs (UnaryExpression unary
)
454 Visit (unary
.Operand
);
457 if (value == null || !Math
.IsType (unary
.Type
, value))
463 void VisitArrayLength (UnaryExpression unary
)
465 Visit (unary
.Operand
);
467 var array
= (Array
) Pop ();
471 void VisitConvert (UnaryExpression unary
)
473 if (unary
.NodeType
== ExpressionType
.ConvertChecked
)
474 VisitConvertChecked (unary
);
476 VisitConvertUnchecked (unary
);
479 void VisitConvertChecked (UnaryExpression unary
)
481 VisitConvert (unary
, Math
.ConvertToTypeChecked
);
484 void VisitConvertUnchecked (UnaryExpression unary
)
486 VisitConvert (unary
, Math
.ConvertToTypeUnchecked
);
489 void VisitConvert (UnaryExpression unary
, Func
<object, Type
, Type
, object> converter
)
491 Visit (unary
.Operand
);
492 Push (converter (Pop (), unary
.Operand
.Type
, unary
.Type
));
495 bool IsNullUnaryLifting (UnaryExpression unary
)
499 if (unary
.IsLifted
&& value == null) {
500 if (unary
.IsLiftedToNull
) {
504 throw new InvalidOperationException ();
512 void VisitQuote (UnaryExpression unary
)
514 Push (unary
.Operand
);
517 void VisitUserDefinedUnary (UnaryExpression unary
)
519 Visit (unary
.Operand
);
521 if (IsNullUnaryLifting (unary
))
526 Push (InvokeUnary (unary
, value));
529 object InvokeUnary (UnaryExpression unary
, object value)
531 return InvokeMethod (unary
.Method
, null, new [] { value }
);
534 void VisitArithmeticUnary (UnaryExpression unary
)
536 Visit (unary
.Operand
);
538 if (IsNullUnaryLifting (unary
))
543 switch (unary
.NodeType
) {
544 case ExpressionType
.Not
:
545 if (unary
.Type
.GetNotNullableType () == typeof (bool))
546 Push (!Convert
.ToBoolean (value));
548 Push (~Convert
.ToInt32 (value));
550 case ExpressionType
.Negate
:
551 Push (Math
.Negate (value, Type
.GetTypeCode (unary
.Type
.GetNotNullableType ())));
553 case ExpressionType
.NegateChecked
:
554 Push (Math
.NegateChecked (value, Type
.GetTypeCode (unary
.Type
.GetNotNullableType ())));
556 case ExpressionType
.UnaryPlus
:
562 protected override void VisitUnary (UnaryExpression unary
)
564 if (unary
.Method
!= null) {
565 VisitUserDefinedUnary (unary
);
569 switch (unary
.NodeType
) {
570 case ExpressionType
.Quote
:
573 case ExpressionType
.TypeAs
:
576 case ExpressionType
.ArrayLength
:
577 VisitArrayLength (unary
);
579 case ExpressionType
.Convert
:
580 case ExpressionType
.ConvertChecked
:
581 VisitConvert (unary
);
583 case ExpressionType
.Negate
:
584 case ExpressionType
.NegateChecked
:
585 case ExpressionType
.Not
:
586 case ExpressionType
.UnaryPlus
:
587 VisitArithmeticUnary (unary
);
590 throw new NotImplementedException (unary
.NodeType
.ToString ());
594 protected override void VisitNew (NewExpression nex
)
596 if (nex
.Constructor
== null)
597 Push (Activator
.CreateInstance (nex
.Type
));
599 Push (InvokeConstructor (nex
.Constructor
, VisitListExpressions (nex
.Arguments
)));
602 static object InvokeConstructor (ConstructorInfo constructor
, object [] arguments
)
605 return constructor
.Invoke (arguments
);
606 } catch (TargetInvocationException e
) {
607 throw e
.InnerException
;
611 protected override void VisitTypeIs (TypeBinaryExpression type
)
613 Visit (type
.Expression
);
614 Push (Math
.IsType (type
.TypeOperand
, Pop ()));
617 void VisitMemberInfo (MemberInfo mi
)
619 mi
.OnFieldOrProperty (
621 object target
= null;
625 Push (field
.GetValue (target
));
628 object target
= null;
629 var getter
= property
.GetGetMethod (true);
630 if (!getter
.IsStatic
)
633 Push (property
.GetValue (target
, null));
637 protected override void VisitMemberAccess (MemberExpression member
)
639 Visit (member
.Expression
);
640 VisitMemberInfo (member
.Member
);
643 protected override void VisitNewArray (NewArrayExpression newArray
)
645 switch (newArray
.NodeType
) {
646 case ExpressionType
.NewArrayInit
:
647 VisitNewArrayInit (newArray
);
649 case ExpressionType
.NewArrayBounds
:
650 VisitNewArrayBounds (newArray
);
654 throw new NotSupportedException ();
657 void VisitNewArrayBounds (NewArrayExpression newArray
)
659 var lengths
= new int [newArray
.Expressions
.Count
];
660 for (int i
= 0; i
< lengths
.Length
; i
++) {
661 Visit (newArray
.Expressions
[i
]);
662 lengths
[i
] = (int) Pop ();
665 Push (Array
.CreateInstance (newArray
.Type
.GetElementType (), lengths
));
668 void VisitNewArrayInit (NewArrayExpression newArray
)
670 var array
= Array
.CreateInstance (
671 newArray
.Type
.GetElementType (),
672 newArray
.Expressions
.Count
);
674 for (int i
= 0; i
< array
.Length
; i
++) {
675 Visit (newArray
.Expressions
[i
]);
676 array
.SetValue (Pop (), i
);
682 protected override void VisitConditional (ConditionalExpression conditional
)
684 Visit (conditional
.Test
);
687 Visit (conditional
.IfTrue
);
689 Visit (conditional
.IfFalse
);
692 protected override void VisitMethodCall (MethodCallExpression call
)
694 object instance
= null;
695 if (call
.Object
!= null) {
700 Push (InvokeMethod (call
.Method
, instance
, VisitListExpressions (call
.Arguments
)));
703 protected override void VisitParameter (ParameterExpression parameter
)
705 for (int i
= 0; i
< lambda
.Parameters
.Count
; i
++) {
706 if (lambda
.Parameters
[i
] != parameter
)
709 Push (arguments
[i
]);
713 throw new ArgumentException ();
716 protected override void VisitConstant (ConstantExpression constant
)
718 Push (constant
.Value
);
721 protected override void VisitInvocation (InvocationExpression invocation
)
723 Visit (invocation
.Expression
);
724 Push (Invoke ((Delegate
) Pop (), VisitListExpressions (invocation
.Arguments
)));
727 static object Invoke (Delegate dlg
, object [] arguments
)
729 return InvokeMethod (dlg
.Method
, dlg
.Target
, arguments
);
732 static object InvokeMethod (MethodBase method
, object obj
, object [] arguments
)
735 return method
.Invoke (obj
, arguments
);
736 } catch (TargetInvocationException e
) {
737 throw e
.InnerException
;
741 protected override void VisitMemberListBinding (MemberListBinding binding
)
745 VisitMemberInfo (binding
.Member
);
746 VisitElementInitializerList (binding
.Initializers
);
747 Pop (); // pop the member
748 Push (value); // push the original target
751 protected override void VisitElementInitializer (ElementInit initializer
)
753 object target
= null;
754 if (!initializer
.AddMethod
.IsStatic
)
757 var arguments
= VisitListExpressions (initializer
.Arguments
);
758 InvokeMethod (initializer
.AddMethod
, target
, arguments
);
760 if (!initializer
.AddMethod
.IsStatic
)
764 protected override void VisitMemberMemberBinding (MemberMemberBinding binding
)
768 VisitMemberInfo (binding
.Member
);
769 VisitBindingList (binding
.Bindings
);
774 protected override void VisitMemberAssignment (MemberAssignment assignment
)
776 Visit (assignment
.Expression
);
780 assignment
.Member
.OnFieldOrProperty (
782 object target
= null;
786 field
.SetValue (target
, value);
792 object target
= null;
793 var getter
= property
.GetGetMethod (true);
794 if (!getter
.IsStatic
)
797 property
.SetValue (target
, value, null);
799 if (!getter
.IsStatic
)
804 protected override void VisitLambda (LambdaExpression lambda
)
806 Push (lambda
.Compile ());
809 private object [] VisitListExpressions (ReadOnlyCollection
<Expression
> collection
)
811 object [] results
= new object [collection
.Count
];
812 for (int i
= 0; i
< results
.Length
; i
++) {
813 Visit (collection
[i
]);
814 results
[i
] = Pop ();
820 public static object Interpret (LambdaExpression lambda
, object [] arguments
)
822 var interpreter
= new ExpressionInterpreter (lambda
, arguments
);
823 interpreter
.Visit (lambda
.Body
);
825 if (lambda
.GetReturnType () != typeof (void))
826 return interpreter
.Pop ();