2010-05-25 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Core / System.Linq.jvm / ExpressionInterpreter.cs
blobf46e177cf06053ce16cd914d87dbd496d2438081
1 //
2 // ExpressionInterpreter.cs
3 //
4 // (C) 2008 Mainsoft, Inc. (http://www.mainsoft.com)
5 // (C) 2008 db4objects, Inc. (http://www.db4o.com)
6 //
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.
27 using System;
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;
39 object [] arguments;
40 Stack<object> stack = new Stack<object> ();
42 void Push (object value)
44 stack.Push (value);
47 object Pop ()
49 return stack.Pop ();
52 public ExpressionInterpreter (LambdaExpression lambda, object [] arguments)
54 this.lambda = lambda;
55 this.arguments = arguments;
58 private void VisitCoalesce (BinaryExpression binary)
60 Visit (binary.Left);
62 var left = Pop ();
64 if (left == null) {
65 Visit (binary.Right);
66 return;
69 if (binary.Conversion == null) {
70 Push (left);
71 return;
74 Push (Invoke (binary.Conversion.Compile (), new [] { left }));
77 void VisitAndAlso (BinaryExpression binary)
79 object left = null;
80 object right = null;
82 Visit (binary.Left);
84 left = Pop ();
86 if (left == null || ((bool) left)) {
87 Visit (binary.Right);
88 right = Pop ();
91 Push (Math.And (left, right));
94 void VisitUserDefinedAndAlso (BinaryExpression binary)
96 object left = null;
97 object right = null;
99 Visit (binary.Left);
101 left = Pop ();
103 if (InvokeFalseOperator (binary, left)) {
104 Push (left);
105 return;
108 Visit (binary.Right);
109 right = Pop ();
111 if (binary.IsLiftedToNull && right == null) {
112 Push (null);
113 return;
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)
141 object left = null;
142 object right = null;
144 Visit (binary.Left);
145 left = Pop ();
147 if (left == null || !((bool) left)) {
148 Visit (binary.Right);
149 right = Pop ();
152 Push (Math.Or (left, right));
155 void VisitUserDefinedOrElse (BinaryExpression binary)
157 object left = null;
158 object right = null;
160 Visit (binary.Left);
161 left = Pop ();
163 if (InvokeTrueOperator (binary, left)) {
164 Push (left);
165 return;
168 Visit (binary.Right);
169 right = Pop ();
171 if (binary.IsLiftedToNull && right == null) {
172 Push (null);
173 return;
176 Push (InvokeMethod (binary.Method, null, new [] { left, right }));
179 void VisitLogicalBinary (BinaryExpression binary)
181 Visit (binary.Left);
182 Visit (binary.Right);
184 var right = Pop ();
185 var left = Pop ();
187 Push (Math.Evaluate (left, right, binary.Type, binary.NodeType));
190 void VisitArithmeticBinary (BinaryExpression binary)
192 Visit (binary.Left);
193 Visit (binary.Right);
195 if (IsNullBinaryLifting (binary))
196 return;
198 var right = Pop ();
199 var left = Pop ();
201 switch (binary.NodeType) {
202 case ExpressionType.RightShift:
203 Push (Math.RightShift (left, Convert.ToInt32 (right), Type.GetTypeCode (binary.Type.GetNotNullableType ())));
204 return;
205 case ExpressionType.LeftShift:
206 Push (Math.LeftShift (left, Convert.ToInt32 (right), Type.GetTypeCode (binary.Type.GetNotNullableType ())));
207 return;
208 default:
209 Push (Math.Evaluate (left, right, binary.Type, binary.NodeType));
210 break;
214 bool IsNullRelationalBinaryLifting (BinaryExpression binary)
216 var right = Pop ();
217 var left = Pop ();
219 if (binary.IsLifted && (left == null || right == null)) {
220 if (binary.IsLiftedToNull) {
221 Push (null);
222 return true;
225 switch (binary.NodeType) {
226 case ExpressionType.Equal:
227 Push (BinaryEqual (binary, left, right));
228 break;
229 case ExpressionType.NotEqual:
230 Push (BinaryNotEqual (binary, left, right));
231 break;
232 default:
233 Push (false);
234 break;
237 return true;
240 Push (left);
241 Push (right);
243 return false;
246 void VisitRelationalBinary (BinaryExpression binary)
248 Visit (binary.Left);
249 Visit (binary.Right);
251 if (IsNullRelationalBinaryLifting (binary))
252 return;
254 var right = Pop ();
255 var left = Pop ();
257 switch (binary.NodeType) {
258 case ExpressionType.Equal:
259 Push (BinaryEqual (binary, left, right));
260 return;
261 case ExpressionType.NotEqual:
262 Push (BinaryNotEqual (binary, left, right));
263 return;
264 case ExpressionType.LessThan:
265 Push (Comparer.Default.Compare (left, right) < 0);
266 return;
267 case ExpressionType.LessThanOrEqual:
268 Push (Comparer.Default.Compare (left, right) <= 0);
269 return;
270 case ExpressionType.GreaterThan:
271 Push (Comparer.Default.Compare (left, right) > 0);
272 return;
273 case ExpressionType.GreaterThanOrEqual:
274 Push (Comparer.Default.Compare (left, right) >= 0);
275 return;
279 void VisitLogicalShortCircuitBinary (BinaryExpression binary)
281 switch (binary.NodeType) {
282 case ExpressionType.AndAlso:
283 VisitAndAlso (binary);
284 return;
285 case ExpressionType.OrElse:
286 VisitOrElse (binary);
287 return;
291 void VisitArrayIndex (BinaryExpression binary)
293 Visit (binary.Left);
294 var left = Pop ();
295 Visit (binary.Right);
296 var right = Pop ();
298 Push (((Array) left).GetValue ((int) right));
301 bool IsNullBinaryLifting (BinaryExpression binary)
303 var right = Pop ();
304 var left = Pop ();
306 if (binary.IsLifted && (right == null || left == null)) {
307 if (binary.IsLiftedToNull)
308 Push (null);
309 else
310 Push (GetDefaultValue (binary.Type));
312 return true;
315 Push (left);
316 Push (right);
318 return false;
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);
333 return;
334 case ExpressionType.Equal:
335 case ExpressionType.NotEqual:
336 VisitUserDefinedRelationalBinary (binary);
337 return;
338 default:
339 VisitUserDefinedCommonBinary (binary);
340 return;
344 void VisitUserDefinedLogicalShortCircuitBinary (BinaryExpression binary)
346 switch (binary.NodeType) {
347 case ExpressionType.AndAlso:
348 VisitUserDefinedAndAlso (binary);
349 return;
350 case ExpressionType.OrElse:
351 VisitUserDefinedOrElse (binary);
352 return;
356 void VisitUserDefinedRelationalBinary (BinaryExpression binary)
358 Visit (binary.Left);
359 Visit (binary.Right);
361 if (IsNullRelationalBinaryLifting (binary))
362 return;
364 var right = Pop ();
365 var left = Pop ();
367 Push (InvokeBinary (binary, left, right));
370 void VisitUserDefinedCommonBinary (BinaryExpression binary)
372 Visit (binary.Left);
373 Visit (binary.Right);
375 if (IsNullBinaryLifting (binary))
376 return;
378 var right = Pop ();
379 var left = Pop ();
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);
393 else
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);
401 else
402 return left != right;
405 protected override void VisitBinary (BinaryExpression binary)
407 if (binary.Method != null) {
408 VisitUserDefinedBinary (binary);
409 return;
412 switch (binary.NodeType) {
413 case ExpressionType.ArrayIndex:
414 VisitArrayIndex (binary);
415 return;
416 case ExpressionType.Coalesce:
417 VisitCoalesce (binary);
418 return;
419 case ExpressionType.AndAlso:
420 case ExpressionType.OrElse:
421 VisitLogicalShortCircuitBinary (binary);
422 return;
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);
430 return;
431 case ExpressionType.And:
432 case ExpressionType.Or:
433 VisitLogicalBinary (binary);
434 return;
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);
448 return;
452 void VisitTypeAs (UnaryExpression unary)
454 Visit (unary.Operand);
456 var value = Pop ();
457 if (value == null || !Math.IsType (unary.Type, value))
458 Push (null);
459 else
460 Push (value);
463 void VisitArrayLength (UnaryExpression unary)
465 Visit (unary.Operand);
467 var array = (Array) Pop ();
468 Push (array.Length);
471 void VisitConvert (UnaryExpression unary)
473 if (unary.NodeType == ExpressionType.ConvertChecked)
474 VisitConvertChecked (unary);
475 else
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)
497 var value = Pop ();
499 if (unary.IsLifted && value == null) {
500 if (unary.IsLiftedToNull) {
501 Push (null);
502 return true;
503 } else {
504 throw new InvalidOperationException ();
508 Push (value);
509 return false;
512 void VisitQuote (UnaryExpression unary)
514 Push (unary.Operand);
517 void VisitUserDefinedUnary (UnaryExpression unary)
519 Visit (unary.Operand);
521 if (IsNullUnaryLifting (unary))
522 return;
524 var value = Pop ();
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))
539 return;
541 var value = Pop ();
543 switch (unary.NodeType) {
544 case ExpressionType.Not:
545 if (unary.Type.GetNotNullableType () == typeof (bool))
546 Push (!Convert.ToBoolean (value));
547 else
548 Push (~Convert.ToInt32 (value));
549 return;
550 case ExpressionType.Negate:
551 Push (Math.Negate (value, Type.GetTypeCode (unary.Type.GetNotNullableType ())));
552 return;
553 case ExpressionType.NegateChecked:
554 Push (Math.NegateChecked (value, Type.GetTypeCode (unary.Type.GetNotNullableType ())));
555 return;
556 case ExpressionType.UnaryPlus:
557 Push (value);
558 return;
562 protected override void VisitUnary (UnaryExpression unary)
564 if (unary.Method != null) {
565 VisitUserDefinedUnary (unary);
566 return;
569 switch (unary.NodeType) {
570 case ExpressionType.Quote:
571 VisitQuote (unary);
572 return;
573 case ExpressionType.TypeAs:
574 VisitTypeAs (unary);
575 return;
576 case ExpressionType.ArrayLength:
577 VisitArrayLength (unary);
578 return;
579 case ExpressionType.Convert:
580 case ExpressionType.ConvertChecked:
581 VisitConvert (unary);
582 return;
583 case ExpressionType.Negate:
584 case ExpressionType.NegateChecked:
585 case ExpressionType.Not:
586 case ExpressionType.UnaryPlus:
587 VisitArithmeticUnary (unary);
588 return;
589 default:
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));
598 else
599 Push (InvokeConstructor (nex.Constructor, VisitListExpressions (nex.Arguments)));
602 static object InvokeConstructor (ConstructorInfo constructor, object [] arguments)
604 try {
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 (
620 field => {
621 object target = null;
622 if (!field.IsStatic)
623 target = Pop ();
625 Push (field.GetValue (target));
627 property => {
628 object target = null;
629 var getter = property.GetGetMethod (true);
630 if (!getter.IsStatic)
631 target = Pop ();
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);
648 return;
649 case ExpressionType.NewArrayBounds:
650 VisitNewArrayBounds (newArray);
651 return;
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);
679 Push (array);
682 protected override void VisitConditional (ConditionalExpression conditional)
684 Visit (conditional.Test);
686 if ((bool) Pop ())
687 Visit (conditional.IfTrue);
688 else
689 Visit (conditional.IfFalse);
692 protected override void VisitMethodCall (MethodCallExpression call)
694 object instance = null;
695 if (call.Object != null) {
696 Visit (call.Object);
697 instance = Pop ();
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)
707 continue;
709 Push (arguments [i]);
710 return;
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)
734 try {
735 return method.Invoke (obj, arguments);
736 } catch (TargetInvocationException e) {
737 throw e.InnerException;
741 protected override void VisitMemberListBinding (MemberListBinding binding)
743 var value = Pop ();
744 Push (value);
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)
755 target = Pop ();
757 var arguments = VisitListExpressions (initializer.Arguments);
758 InvokeMethod (initializer.AddMethod, target, arguments);
760 if (!initializer.AddMethod.IsStatic)
761 Push (target);
764 protected override void VisitMemberMemberBinding (MemberMemberBinding binding)
766 var value = Pop ();
767 Push (value);
768 VisitMemberInfo (binding.Member);
769 VisitBindingList (binding.Bindings);
770 Pop ();
771 Push (value);
774 protected override void VisitMemberAssignment (MemberAssignment assignment)
776 Visit (assignment.Expression);
778 var value = Pop ();
780 assignment.Member.OnFieldOrProperty (
781 field => {
782 object target = null;
783 if (!field.IsStatic)
784 target = Pop ();
786 field.SetValue (target, value);
788 if (!field.IsStatic)
789 Push (target);
791 property => {
792 object target = null;
793 var getter = property.GetGetMethod (true);
794 if (!getter.IsStatic)
795 target = Pop ();
797 property.SetValue (target, value, null);
799 if (!getter.IsStatic)
800 Push (target);
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 ();
817 return results;
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 ();
828 return null;