2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.Expressions.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Microsoft Public License. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Microsoft Public License, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Microsoft Public License.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.Diagnostics;
21 #if CODEPLEX_40
22 using System.Dynamic.Utils;
23 #else
24 using Microsoft.Scripting.Utils;
25 #endif
26 using System.Reflection;
27 using System.Reflection.Emit;
28 using System.Runtime.CompilerServices;
29 #if !CODEPLEX_40
30 using Microsoft.Runtime.CompilerServices;
31 #endif
32
33
34 #if CODEPLEX_40
35 namespace System.Linq.Expressions.Compiler {
36 #else
37 namespace Microsoft.Linq.Expressions.Compiler {
38 #endif
39     partial class LambdaCompiler {
40         [Flags]
41         internal enum CompilationFlags {
42             EmitExpressionStart = 0x0001,
43             EmitNoExpressionStart = 0x0002,
44             EmitAsDefaultType = 0x0010,
45             EmitAsVoidType = 0x0020,
46             EmitAsTail = 0x0100,   // at the tail position of a lambda, tail call can be safely emitted
47             EmitAsMiddle = 0x0200, // in the middle of a lambda, tail call can be emitted if it is in a return
48             EmitAsNoTail = 0x0400, // neither at the tail or in a return, or tail call is not turned on, no tail call is emitted
49
50             EmitExpressionStartMask = 0x000f,
51             EmitAsTypeMask = 0x00f0,
52             EmitAsTailCallMask = 0x0f00
53         }
54
55         /// <summary>
56         /// Update the flag with a new EmitAsTailCall flag
57         /// </summary>
58         private static CompilationFlags UpdateEmitAsTailCallFlag(CompilationFlags flags, CompilationFlags newValue) {
59             Debug.Assert(newValue == CompilationFlags.EmitAsTail || newValue == CompilationFlags.EmitAsMiddle || newValue == CompilationFlags.EmitAsNoTail);
60             var oldValue = flags & CompilationFlags.EmitAsTailCallMask;
61             return flags ^ oldValue | newValue;
62         }
63
64         /// <summary>
65         /// Update the flag with a new EmitExpressionStart flag
66         /// </summary>
67         private static CompilationFlags UpdateEmitExpressionStartFlag(CompilationFlags flags, CompilationFlags newValue) {
68             Debug.Assert(newValue == CompilationFlags.EmitExpressionStart || newValue == CompilationFlags.EmitNoExpressionStart);
69             var oldValue = flags & CompilationFlags.EmitExpressionStartMask;
70             return flags ^ oldValue | newValue;
71         }
72
73         /// <summary>
74         /// Update the flag with a new EmitAsType flag
75         /// </summary>
76         private static CompilationFlags UpdateEmitAsTypeFlag(CompilationFlags flags, CompilationFlags newValue) {
77             Debug.Assert(newValue == CompilationFlags.EmitAsDefaultType || newValue == CompilationFlags.EmitAsVoidType);
78             var oldValue = flags & CompilationFlags.EmitAsTypeMask;
79             return flags ^ oldValue | newValue;
80         }
81
82         /// <summary>
83         /// Generates code for this expression in a value position.
84         /// This method will leave the value of the expression
85         /// on the top of the stack typed as Type.
86         /// </summary>
87         internal void EmitExpression(Expression node) {
88             EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitExpressionStart);
89         }
90
91         /// <summary>
92         /// Emits an expression and discards the result.  For some nodes this emits
93         /// more optimial code then EmitExpression/Pop
94         /// </summary>
95         private void EmitExpressionAsVoid(Expression node) {
96             EmitExpressionAsVoid(node, CompilationFlags.EmitAsNoTail);
97         }
98
99         private void EmitExpressionAsVoid(Expression node, CompilationFlags flags) {
100             Debug.Assert(node != null);
101
102             CompilationFlags startEmitted = EmitExpressionStart(node);
103
104             switch (node.NodeType) {
105                 case ExpressionType.Assign:
106                     EmitAssign((BinaryExpression)node, CompilationFlags.EmitAsVoidType);
107                     break;
108                 case ExpressionType.Block:
109                     Emit((BlockExpression)node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
110                     break;
111                 case ExpressionType.Throw:
112                     EmitThrow((UnaryExpression)node, CompilationFlags.EmitAsVoidType);
113                     break;
114                 case ExpressionType.Goto:
115                     EmitGotoExpression(node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
116                     break;
117                 case ExpressionType.Constant:
118                 case ExpressionType.Default:
119                 case ExpressionType.Parameter:
120                     // no-op
121                     break;
122                 default:
123                     if (node.Type == typeof(void)) {
124                         EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitNoExpressionStart));
125                     } else {
126                         EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
127                         _ilg.Emit(OpCodes.Pop);
128                     }
129                     break;
130             }
131             EmitExpressionEnd(startEmitted);
132         }
133
134         private void EmitExpressionAsType(Expression node, Type type, CompilationFlags flags) {
135
136             if (type == typeof(void)) {
137                 EmitExpressionAsVoid(node, flags);
138             } else {
139                 // if the node is emitted as a different type, CastClass IL is emitted at the end,
140                 // should not emit with tail calls.
141                 if (!TypeUtils.AreEquivalent(node.Type, type)) {
142                     EmitExpression(node);
143                     Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type));
144                     _ilg.Emit(OpCodes.Castclass, type);
145                 } else {
146                     // emit the with the flags and emit emit expression start
147                     EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart));
148                 }
149             }
150         }
151
152         #region label block tracking
153
154         private CompilationFlags EmitExpressionStart(Expression node) {
155             if (TryPushLabelBlock(node)) {
156                 return CompilationFlags.EmitExpressionStart;
157             }
158             return CompilationFlags.EmitNoExpressionStart;
159         }
160
161         private void EmitExpressionEnd(CompilationFlags flags) {
162             if ((flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart) {
163                 PopLabelBlock(_labelBlock.Kind);
164             }
165         }
166
167         #endregion
168
169         #region InvocationExpression
170
171         private void EmitInvocationExpression(Expression expr, CompilationFlags flags) {
172             InvocationExpression node = (InvocationExpression)expr;
173
174             // Optimization: inline code for literal lambda's directly
175             //
176             // This is worth it because otherwise we end up with a extra call
177             // to DynamicMethod.CreateDelegate, which is expensive.
178             //
179             if (node.LambdaOperand != null) {
180                 EmitInlinedInvoke(node, flags);
181                 return;
182             }
183
184             expr = node.Expression;
185             if (typeof(LambdaExpression).IsAssignableFrom(expr.Type)) {
186                 // if the invoke target is a lambda expression tree, first compile it into a delegate
187                 expr = Expression.Call(expr, expr.Type.GetMethod("Compile", new Type[0]));
188             }
189             expr = Expression.Call(expr, expr.Type.GetMethod("Invoke"), node.Arguments);
190
191             EmitExpression(expr);
192         }
193
194         private void EmitInlinedInvoke(InvocationExpression invoke, CompilationFlags flags) {
195             var lambda = invoke.LambdaOperand;
196
197             // This is tricky: we need to emit the arguments outside of the
198             // scope, but set them inside the scope. Fortunately, using the IL
199             // stack it is entirely doable.
200
201             // 1. Emit invoke arguments
202             List<WriteBack> wb = EmitArguments(lambda.Type.GetMethod("Invoke"), invoke);
203
204             // 2. Create the nested LambdaCompiler
205             var inner = new LambdaCompiler(this, lambda);
206
207             // 3. Emit the body
208             // if the inlined lambda is the last expression of the whole lambda,
209             // tail call can be applied.
210             if (wb.Count != 0) {
211                 flags = UpdateEmitAsTailCallFlag(flags, CompilationFlags.EmitAsNoTail);
212             }
213             inner.EmitLambdaBody(_scope, true, flags);
214
215             // 4. Emit writebacks if needed
216             EmitWriteBack(wb);
217         }
218
219         #endregion
220
221         #region IndexExpression
222
223         private void EmitIndexExpression(Expression expr) {
224             var node = (IndexExpression)expr;
225
226             // Emit instance, if calling an instance method
227             Type objectType = null;
228             if (node.Object != null) {
229                 EmitInstance(node.Object, objectType = node.Object.Type);
230             }
231
232             // Emit indexes. We don't allow byref args, so no need to worry
233             // about writebacks or EmitAddress
234             foreach (var arg in node.Arguments) {
235                 EmitExpression(arg);
236             }
237
238             EmitGetIndexCall(node, objectType);
239         }
240
241         private void EmitIndexAssignment(BinaryExpression node, CompilationFlags flags) {
242             var index = (IndexExpression)node.Left;
243
244             var emitAs = flags & CompilationFlags.EmitAsTypeMask;
245
246             // Emit instance, if calling an instance method
247             Type objectType = null;
248             if (index.Object != null) {
249                 EmitInstance(index.Object, objectType = index.Object.Type);
250             }
251
252             // Emit indexes. We don't allow byref args, so no need to worry
253             // about writebacks or EmitAddress
254             foreach (var arg in index.Arguments) {
255                 EmitExpression(arg);
256             }
257
258             // Emit value
259             EmitExpression(node.Right);
260
261             // Save the expression value, if needed
262             LocalBuilder temp = null;
263             if (emitAs != CompilationFlags.EmitAsVoidType) {
264                 _ilg.Emit(OpCodes.Dup);
265                 _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type));
266             }
267
268             EmitSetIndexCall(index, objectType);
269
270             // Restore the value
271             if (emitAs != CompilationFlags.EmitAsVoidType) {
272                 _ilg.Emit(OpCodes.Ldloc, temp);
273                 FreeLocal(temp);
274             }
275         }
276
277         private void EmitGetIndexCall(IndexExpression node, Type objectType) {
278             if (node.Indexer != null) {
279                 // For indexed properties, just call the getter
280                 var method = node.Indexer.GetGetMethod(true);
281                 EmitCall(objectType, method);
282             } else if (node.Arguments.Count != 1) {
283                 // Multidimensional arrays, call get
284                 _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance));
285             } else {
286                 // For one dimensional arrays, emit load
287                 _ilg.EmitLoadElement(node.Type);
288             }
289         }
290
291         private void EmitSetIndexCall(IndexExpression node, Type objectType) {
292             if (node.Indexer != null) {
293                 // For indexed properties, just call the setter
294                 var method = node.Indexer.GetSetMethod(true);
295                 EmitCall(objectType, method);
296             } else if (node.Arguments.Count != 1) {
297                 // Multidimensional arrays, call set
298                 _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance));
299             } else {
300                 // For one dimensional arrays, emit store
301                 _ilg.EmitStoreElement(node.Type);
302             }
303         }
304
305         #endregion
306
307         #region MethodCallExpression
308
309         private void EmitMethodCallExpression(Expression expr, CompilationFlags flags) {
310             MethodCallExpression node = (MethodCallExpression)expr;
311
312             EmitMethodCall(node.Object, node.Method, node, flags);
313         }
314
315         private void EmitMethodCallExpression(Expression expr) {
316             EmitMethodCallExpression(expr, CompilationFlags.EmitAsNoTail);
317         }
318
319         private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr) {
320             EmitMethodCall(obj, method, methodCallExpr, CompilationFlags.EmitAsNoTail);
321         }
322
323         private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr, CompilationFlags flags) {
324             // Emit instance, if calling an instance method
325             Type objectType = null;
326             if (!method.IsStatic) {
327                 EmitInstance(obj, objectType = obj.Type);
328             }
329             // if the obj has a value type, its address is passed to the method call so we cannot destroy the 
330             // stack by emitting a tail call
331             if (obj != null && obj.Type.IsValueType) {
332                 EmitMethodCall(method, methodCallExpr, objectType);
333             } else {
334                 EmitMethodCall(method, methodCallExpr, objectType, flags);
335             }
336         }
337
338         // assumes 'object' of non-static call is already on stack
339         private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType) {
340             EmitMethodCall(mi, args, objectType, CompilationFlags.EmitAsNoTail);
341         }
342
343         // assumes 'object' of non-static call is already on stack
344         private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType, CompilationFlags flags) {
345
346             // Emit arguments
347             List<WriteBack> wb = EmitArguments(mi, args);
348
349             // Emit the actual call
350             OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call;
351             if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
352                 // This automatically boxes value types if necessary.
353                 _ilg.Emit(OpCodes.Constrained, objectType);
354             }
355             // The method call can be a tail call if 
356             // 1) the method call is the last instruction before Ret
357             // 2) the method does not have any ByRef parameters, refer to ECMA-335 Partition III Section 2.4.
358             //    "Verification requires that no managed pointers are passed to the method being called, since
359             //    it does not track pointers into the current frame."
360             if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail && !MethodHasByRefParameter(mi)) {
361                 _ilg.Emit(OpCodes.Tailcall);
362             }
363             if (mi.CallingConvention == CallingConventions.VarArgs) {
364                 _ilg.EmitCall(callOp, mi, args.Map(a => a.Type));
365             } else {
366                 _ilg.Emit(callOp, mi);
367             }
368
369             // Emit writebacks for properties passed as "ref" arguments
370             EmitWriteBack(wb);
371         }
372
373         private static bool MethodHasByRefParameter(MethodInfo mi) {
374             foreach (var pi in mi.GetParametersCached()) {
375                 if (pi.IsByRefParameter()) {
376                     return true;
377                 }
378             }
379             return false;
380         }
381
382         private void EmitCall(Type objectType, MethodInfo method) {
383             if (method.CallingConvention == CallingConventions.VarArgs) {
384                 throw Error.UnexpectedVarArgsCall(method);
385             }
386
387             OpCode callOp = UseVirtual(method) ? OpCodes.Callvirt : OpCodes.Call;
388             if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
389                 _ilg.Emit(OpCodes.Constrained, objectType);
390             }
391             _ilg.Emit(callOp, method);
392         }
393
394         private static bool UseVirtual(MethodInfo mi) {
395             // There are two factors: is the method static, virtual or non-virtual instance?
396             // And is the object ref or value?
397             // The cases are:
398             //
399             // static, ref:     call
400             // static, value:   call
401             // virtual, ref:    callvirt
402             // virtual, value:  call -- eg, double.ToString must be a non-virtual call to be verifiable.
403             // instance, ref:   callvirt -- this looks wrong, but is verifiable and gives us a free null check.
404             // instance, value: call
405             //
406             // We never need to generate a nonvirtual call to a virtual method on a reference type because
407             // expression trees do not support "base.Foo()" style calling.
408             // 
409             // We could do an optimization here for the case where we know that the object is a non-null
410             // reference type and the method is a non-virtual instance method.  For example, if we had
411             // (new Foo()).Bar() for instance method Bar we don't need the null check so we could do a
412             // call rather than a callvirt.  However that seems like it would not be a very big win for
413             // most dynamically generated code scenarios, so let's not do that for now.
414
415             if (mi.IsStatic) {
416                 return false;
417             }
418             if (mi.DeclaringType.IsValueType) {
419                 return false;
420             }
421             return true;
422         }
423
424         /// <summary>
425         /// Emits arguments to a call, and returns an array of writebacks that
426         /// should happen after the call.
427         /// </summary>
428         private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args) {
429             return EmitArguments(method, args, 0);
430         }
431
432         /// <summary>
433         /// Emits arguments to a call, and returns an array of writebacks that
434         /// should happen after the call. For emitting dynamic expressions, we
435         /// need to skip the first parameter of the method (the call site).
436         /// </summary>
437         private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args, int skipParameters) {
438             ParameterInfo[] pis = method.GetParametersCached();
439             Debug.Assert(args.ArgumentCount + skipParameters == pis.Length);
440
441             var writeBacks = new List<WriteBack>();
442             for (int i = skipParameters, n = pis.Length; i < n; i++) {
443                 ParameterInfo parameter = pis[i];
444                 Expression argument = args.GetArgument(i - skipParameters);
445                 Type type = parameter.ParameterType;
446
447                 if (type.IsByRef) {
448                     type = type.GetElementType();
449
450                     WriteBack wb = EmitAddressWriteBack(argument, type);
451                     if (wb != null) {
452                         writeBacks.Add(wb);
453                     }
454                 } else {
455                     EmitExpression(argument);
456                 }
457             }
458             return writeBacks;
459         }
460
461         private static void EmitWriteBack(IList<WriteBack> writeBacks) {
462             foreach (WriteBack wb in writeBacks) {
463                 wb();
464             }
465         }
466
467         #endregion
468
469         private void EmitConstantExpression(Expression expr) {
470             ConstantExpression node = (ConstantExpression)expr;
471
472             EmitConstant(node.Value, node.Type);
473         }
474
475         private void EmitConstant(object value, Type type) {
476             // Try to emit the constant directly into IL
477             if (ILGen.CanEmitConstant(value, type)) {
478                 _ilg.EmitConstant(value, type);
479                 return;
480             }
481
482             _boundConstants.EmitConstant(this, value, type);
483         }
484
485         private void EmitDynamicExpression(Expression expr) {
486             if (!(_method is DynamicMethod)) {
487                 throw Error.CannotCompileDynamic();
488             }
489
490             var node = (DynamicExpression)expr;
491
492             var site = CallSite.Create(node.DelegateType, node.Binder);
493             Type siteType = site.GetType();
494
495             var invoke = node.DelegateType.GetMethod("Invoke");
496
497             // site.Target.Invoke(site, args)
498             EmitConstant(site, siteType);
499
500             // Emit the temp as type CallSite so we get more reuse
501             _ilg.Emit(OpCodes.Dup);
502 #if MICROSOFT_SCRIPTING_CORE
503             // For 3.5, emit the temp as CallSite<T> to work around a Jit32
504             // verifier issue (fixed in 3.5 sp1)
505             var siteTemp = GetLocal(siteType);
506 #else
507             var siteTemp = GetLocal(typeof(CallSite));
508 #endif
509             _ilg.Emit(OpCodes.Stloc, siteTemp);
510             _ilg.Emit(OpCodes.Ldfld, siteType.GetField("Target"));
511             _ilg.Emit(OpCodes.Ldloc, siteTemp);
512             FreeLocal(siteTemp);
513
514             List<WriteBack> wb = EmitArguments(invoke, node, 1);
515             _ilg.Emit(OpCodes.Callvirt, invoke);
516             EmitWriteBack(wb);
517         }
518
519         private void EmitNewExpression(Expression expr) {
520             NewExpression node = (NewExpression)expr;
521
522             if (node.Constructor != null) {
523                 List<WriteBack> wb = EmitArguments(node.Constructor, node);
524                 _ilg.Emit(OpCodes.Newobj, node.Constructor);
525                 EmitWriteBack(wb);
526             } else {
527                 Debug.Assert(node.Arguments.Count == 0, "Node with arguments must have a constructor.");
528                 Debug.Assert(node.Type.IsValueType, "Only value type may have constructor not set.");
529                 LocalBuilder temp = GetLocal(node.Type);
530                 _ilg.Emit(OpCodes.Ldloca, temp);
531                 _ilg.Emit(OpCodes.Initobj, node.Type);
532                 _ilg.Emit(OpCodes.Ldloc, temp);
533                 FreeLocal(temp);
534             }
535         }
536
537         private void EmitTypeBinaryExpression(Expression expr) {
538             TypeBinaryExpression node = (TypeBinaryExpression)expr;
539
540             if (node.NodeType == ExpressionType.TypeEqual) {
541                 EmitExpression(node.ReduceTypeEqual());
542                 return;
543             }
544
545             Type type = node.Expression.Type;
546
547             // Try to determine the result statically
548             AnalyzeTypeIsResult result = ConstantCheck.AnalyzeTypeIs(node);
549
550             if (result == AnalyzeTypeIsResult.KnownTrue ||
551                 result == AnalyzeTypeIsResult.KnownFalse) {
552                 // Result is known statically, so just emit the expression for
553                 // its side effects and return the result
554                 EmitExpressionAsVoid(node.Expression);
555                 _ilg.EmitBoolean(result == AnalyzeTypeIsResult.KnownTrue);
556                 return;
557             }
558
559             if (result == AnalyzeTypeIsResult.KnownAssignable) {
560                 // We know the type can be assigned, but still need to check
561                 // for null at runtime
562                 if (type.IsNullableType()) {
563                     EmitAddress(node.Expression, type);
564                     _ilg.EmitHasValue(type);
565                     return;
566                 }
567
568                 Debug.Assert(!type.IsValueType);
569                 EmitExpression(node.Expression);
570                 _ilg.Emit(OpCodes.Ldnull);
571                 _ilg.Emit(OpCodes.Ceq);
572                 _ilg.Emit(OpCodes.Ldc_I4_0);
573                 _ilg.Emit(OpCodes.Ceq);
574                 return;
575             }
576
577             Debug.Assert(result == AnalyzeTypeIsResult.Unknown);
578
579             // Emit a full runtime "isinst" check
580             EmitExpression(node.Expression);
581             if (type.IsValueType) {
582                 _ilg.Emit(OpCodes.Box, type);
583             }
584             _ilg.Emit(OpCodes.Isinst, node.TypeOperand);
585             _ilg.Emit(OpCodes.Ldnull);
586             _ilg.Emit(OpCodes.Cgt_Un);
587         }
588
589         private void EmitVariableAssignment(BinaryExpression node, CompilationFlags flags) {
590             var variable = (ParameterExpression)node.Left;
591             var emitAs = flags & CompilationFlags.EmitAsTypeMask;
592
593             EmitExpression(node.Right);
594             if (emitAs != CompilationFlags.EmitAsVoidType) {
595                 _ilg.Emit(OpCodes.Dup);
596             }
597
598             if (variable.IsByRef) {
599                 // Note: the stloc/ldloc pattern is a bit suboptimal, but it
600                 // saves us from having to spill stack when assigning to a
601                 // byref parameter. We already make this same tradeoff for
602                 // hoisted variables, see ElementStorage.EmitStore
603
604                 LocalBuilder value = GetLocal(variable.Type);
605                 _ilg.Emit(OpCodes.Stloc, value);
606                 _scope.EmitGet(variable);
607                 _ilg.Emit(OpCodes.Ldloc, value);
608                 FreeLocal(value);
609                 _ilg.EmitStoreValueIndirect(variable.Type);
610             } else {
611                 _scope.EmitSet(variable);
612             }
613         }
614
615         private void EmitAssignBinaryExpression(Expression expr) {
616             EmitAssign((BinaryExpression)expr, CompilationFlags.EmitAsDefaultType);
617         }
618
619         private void EmitAssign(BinaryExpression node, CompilationFlags emitAs) {
620             switch (node.Left.NodeType) {
621                 case ExpressionType.Index:
622                     EmitIndexAssignment(node, emitAs);
623                     return;
624                 case ExpressionType.MemberAccess:
625                     EmitMemberAssignment(node, emitAs);
626                     return;
627                 case ExpressionType.Parameter:
628                     EmitVariableAssignment(node, emitAs);
629                     return;
630                 default:
631                     throw Error.InvalidLvalue(node.Left.NodeType);
632             }
633         }
634
635         private void EmitParameterExpression(Expression expr) {
636             ParameterExpression node = (ParameterExpression)expr;
637             _scope.EmitGet(node);
638             if (node.IsByRef) {
639                 _ilg.EmitLoadValueIndirect(node.Type);
640             }
641         }
642
643         private void EmitLambdaExpression(Expression expr) {
644             LambdaExpression node = (LambdaExpression)expr;
645             EmitDelegateConstruction(node);
646         }
647
648         private void EmitRuntimeVariablesExpression(Expression expr) {
649             RuntimeVariablesExpression node = (RuntimeVariablesExpression)expr;
650             _scope.EmitVariableAccess(this, node.Variables);
651         }
652
653         private void EmitMemberAssignment(BinaryExpression node, CompilationFlags flags) {
654             MemberExpression lvalue = (MemberExpression)node.Left;
655             MemberInfo member = lvalue.Member;
656
657             // emit "this", if any
658             Type objectType = null;
659             if (lvalue.Expression != null) {
660                 EmitInstance(lvalue.Expression, objectType = lvalue.Expression.Type);
661             }
662
663             // emit value
664             EmitExpression(node.Right);
665
666             LocalBuilder temp = null;
667             var emitAs = flags & CompilationFlags.EmitAsTypeMask;
668             if (emitAs != CompilationFlags.EmitAsVoidType) {
669                 // save the value so we can return it
670                 _ilg.Emit(OpCodes.Dup);
671                 _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type));
672             }
673
674             switch (member.MemberType) {
675                 case MemberTypes.Field:
676                     _ilg.EmitFieldSet((FieldInfo)member);
677                     break;
678                 case MemberTypes.Property:
679                     EmitCall(objectType, ((PropertyInfo)member).GetSetMethod(true));
680                     break;
681                 default:
682                     throw Error.InvalidMemberType(member.MemberType);
683             }
684
685             if (emitAs != CompilationFlags.EmitAsVoidType) {
686                 _ilg.Emit(OpCodes.Ldloc, temp);
687                 FreeLocal(temp);
688             }
689         }
690
691         private void EmitMemberExpression(Expression expr) {
692             MemberExpression node = (MemberExpression)expr;
693
694             // emit "this", if any
695             Type instanceType = null;
696             if (node.Expression != null) {
697                 EmitInstance(node.Expression, instanceType = node.Expression.Type);
698             }
699
700             EmitMemberGet(node.Member, instanceType);
701         }
702
703         // assumes instance is already on the stack
704         private void EmitMemberGet(MemberInfo member, Type objectType) {
705             switch (member.MemberType) {
706                 case MemberTypes.Field:
707                     FieldInfo fi = (FieldInfo)member;
708                     if (fi.IsLiteral) {
709                         EmitConstant(fi.GetRawConstantValue(), fi.FieldType);
710                     } else {
711                         _ilg.EmitFieldGet(fi);
712                     }
713                     break;
714                 case MemberTypes.Property:
715                     EmitCall(objectType, ((PropertyInfo)member).GetGetMethod(true));
716                     break;
717                 default:
718                     throw ContractUtils.Unreachable;
719             }
720         }
721
722         private void EmitInstance(Expression instance, Type type) {
723             if (instance != null) {
724                 if (type.IsValueType) {
725                     EmitAddress(instance, type);
726                 } else {
727                     EmitExpression(instance);
728                 }
729             }
730         }
731
732         private void EmitNewArrayExpression(Expression expr) {
733             NewArrayExpression node = (NewArrayExpression)expr;
734
735             if (node.NodeType == ExpressionType.NewArrayInit) {
736                 _ilg.EmitArray(
737                     node.Type.GetElementType(),
738                     node.Expressions.Count,
739                     delegate(int index) {
740                         EmitExpression(node.Expressions[index]);
741                     }
742                 );
743             } else {
744                 ReadOnlyCollection<Expression> bounds = node.Expressions;
745                 for (int i = 0; i < bounds.Count; i++) {
746                     Expression x = bounds[i];
747                     EmitExpression(x);
748                     _ilg.EmitConvertToType(x.Type, typeof(int), true);
749                 }
750                 _ilg.EmitArray(node.Type);
751             }
752         }
753
754         private void EmitDebugInfoExpression(Expression expr) {
755             if (!EmitDebugSymbols) {
756                 return;
757             }
758             var node = (DebugInfoExpression)expr;
759
760             if (node.IsClear && _sequencePointCleared) {
761                 // Emitting another clearance after one clearance does not
762                 // have any effect, so we can save it.
763                 return;
764             }
765
766             _tree.DebugInfoGenerator.MarkSequencePoint(_lambda, _method, _ilg, node);
767             _ilg.Emit(OpCodes.Nop);
768             _sequencePointCleared = node.IsClear;
769         }
770
771         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expr")]
772         private static void EmitExtensionExpression(Expression expr) {
773             throw Error.ExtensionNotReduced();
774         }
775
776         #region ListInit, MemberInit
777
778         private void EmitListInitExpression(Expression expr) {
779             EmitListInit((ListInitExpression)expr);
780         }
781
782         private void EmitMemberInitExpression(Expression expr) {
783             EmitMemberInit((MemberInitExpression)expr);
784         }
785
786         private void EmitBinding(MemberBinding binding, Type objectType) {
787             switch (binding.BindingType) {
788                 case MemberBindingType.Assignment:
789                     EmitMemberAssignment((MemberAssignment)binding, objectType);
790                     break;
791                 case MemberBindingType.ListBinding:
792                     EmitMemberListBinding((MemberListBinding)binding);
793                     break;
794                 case MemberBindingType.MemberBinding:
795                     EmitMemberMemberBinding((MemberMemberBinding)binding);
796                     break;
797                 default:
798                     throw Error.UnknownBindingType();
799             }
800         }
801
802         private void EmitMemberAssignment(MemberAssignment binding, Type objectType) {
803             EmitExpression(binding.Expression);
804             FieldInfo fi = binding.Member as FieldInfo;
805             if (fi != null) {
806                 _ilg.Emit(OpCodes.Stfld, fi);
807             } else {
808                 PropertyInfo pi = binding.Member as PropertyInfo;
809                 if (pi != null) {
810                     EmitCall(objectType, pi.GetSetMethod(true));
811                 } else {
812                     throw Error.UnhandledBinding();
813                 }
814             }
815         }
816
817         private void EmitMemberMemberBinding(MemberMemberBinding binding) {
818             Type type = GetMemberType(binding.Member);
819             if (binding.Member is PropertyInfo && type.IsValueType) {
820                 throw Error.CannotAutoInitializeValueTypeMemberThroughProperty(binding.Member);
821             }
822             if (type.IsValueType) {
823                 EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
824             } else {
825                 EmitMemberGet(binding.Member, binding.Member.DeclaringType);
826             }
827             EmitMemberInit(binding.Bindings, false, type);
828         }
829
830         private void EmitMemberListBinding(MemberListBinding binding) {
831             Type type = GetMemberType(binding.Member);
832             if (binding.Member is PropertyInfo && type.IsValueType) {
833                 throw Error.CannotAutoInitializeValueTypeElementThroughProperty(binding.Member);
834             }
835             if (type.IsValueType) {
836                 EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
837             } else {
838                 EmitMemberGet(binding.Member, binding.Member.DeclaringType);
839             }
840             EmitListInit(binding.Initializers, false, type);
841         }
842
843         private void EmitMemberInit(MemberInitExpression init) {
844             EmitExpression(init.NewExpression);
845             LocalBuilder loc = null;
846             if (init.NewExpression.Type.IsValueType && init.Bindings.Count > 0) {
847                 loc = _ilg.DeclareLocal(init.NewExpression.Type);
848                 _ilg.Emit(OpCodes.Stloc, loc);
849                 _ilg.Emit(OpCodes.Ldloca, loc);
850             }
851             EmitMemberInit(init.Bindings, loc == null, init.NewExpression.Type);
852             if (loc != null) {
853                 _ilg.Emit(OpCodes.Ldloc, loc);
854             }
855         }
856
857         // This method assumes that the instance is on the stack and is expected, based on "keepOnStack" flag
858         // to either leave the instance on the stack, or pop it.
859         private void EmitMemberInit(ReadOnlyCollection<MemberBinding> bindings, bool keepOnStack, Type objectType) {
860             int n = bindings.Count;
861             if (n == 0) {
862                 // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
863                 if (!keepOnStack) {
864                     _ilg.Emit(OpCodes.Pop);
865                 }
866             } else {
867                 for (int i = 0; i < n; i++) {
868                     if (keepOnStack || i < n - 1) {
869                         _ilg.Emit(OpCodes.Dup);
870                     }
871                     EmitBinding(bindings[i], objectType);
872                 }
873             }
874         }
875
876         private void EmitListInit(ListInitExpression init) {
877             EmitExpression(init.NewExpression);
878             LocalBuilder loc = null;
879             if (init.NewExpression.Type.IsValueType) {
880                 loc = _ilg.DeclareLocal(init.NewExpression.Type);
881                 _ilg.Emit(OpCodes.Stloc, loc);
882                 _ilg.Emit(OpCodes.Ldloca, loc);
883             }
884             EmitListInit(init.Initializers, loc == null, init.NewExpression.Type);
885             if (loc != null) {
886                 _ilg.Emit(OpCodes.Ldloc, loc);
887             }
888         }
889
890         // This method assumes that the list instance is on the stack and is expected, based on "keepOnStack" flag
891         // to either leave the list instance on the stack, or pop it.
892         private void EmitListInit(ReadOnlyCollection<ElementInit> initializers, bool keepOnStack, Type objectType) {
893             int n = initializers.Count;
894
895             if (n == 0) {
896                 // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
897                 if (!keepOnStack) {
898                     _ilg.Emit(OpCodes.Pop);
899                 }
900             } else {
901                 for (int i = 0; i < n; i++) {
902                     if (keepOnStack || i < n - 1) {
903                         _ilg.Emit(OpCodes.Dup);
904                     }
905                     EmitMethodCall(initializers[i].AddMethod, initializers[i], objectType);
906
907                     // Aome add methods, ArrayList.Add for example, return non-void
908                     if (initializers[i].AddMethod.ReturnType != typeof(void)) {
909                         _ilg.Emit(OpCodes.Pop);
910                     }
911                 }
912             }
913         }
914
915         private static Type GetMemberType(MemberInfo member) {
916             FieldInfo fi = member as FieldInfo;
917             if (fi != null) return fi.FieldType;
918             PropertyInfo pi = member as PropertyInfo;
919             if (pi != null) return pi.PropertyType;
920             throw Error.MemberNotFieldOrProperty(member);
921         }
922
923         #endregion
924
925         #region Expression helpers
926
927         internal static void ValidateLift(IList<ParameterExpression> variables, IList<Expression> arguments) {
928             System.Diagnostics.Debug.Assert(variables != null);
929             System.Diagnostics.Debug.Assert(arguments != null);
930
931             if (variables.Count != arguments.Count) {
932                 throw Error.IncorrectNumberOfIndexes();
933             }
934             for (int i = 0, n = variables.Count; i < n; i++) {
935                 if (!TypeUtils.AreReferenceAssignable(variables[i].Type, TypeUtils.GetNonNullableType(arguments[i].Type))) {
936                     throw Error.ArgumentTypesMustMatch();
937                 }
938             }
939         }
940
941         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
942         private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpression mc, ParameterExpression[] paramList, Expression[] argList) {
943             Debug.Assert(TypeUtils.AreEquivalent(TypeUtils.GetNonNullableType(resultType), TypeUtils.GetNonNullableType(mc.Type)));
944
945             switch (nodeType) {
946                 default:
947                 case ExpressionType.LessThan:
948                 case ExpressionType.LessThanOrEqual:
949                 case ExpressionType.GreaterThan:
950                 case ExpressionType.GreaterThanOrEqual: {
951                         Label exit = _ilg.DefineLabel();
952                         Label exitNull = _ilg.DefineLabel();
953                         LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool));
954                         for (int i = 0, n = paramList.Length; i < n; i++) {
955                             ParameterExpression v = paramList[i];
956                             Expression arg = argList[i];
957                             if (TypeUtils.IsNullableType(arg.Type)) {
958                                 _scope.AddLocal(this, v);
959                                 EmitAddress(arg, arg.Type);
960                                 _ilg.Emit(OpCodes.Dup);
961                                 _ilg.EmitHasValue(arg.Type);
962                                 _ilg.Emit(OpCodes.Ldc_I4_0);
963                                 _ilg.Emit(OpCodes.Ceq);
964                                 _ilg.Emit(OpCodes.Stloc, anyNull);
965                                 _ilg.EmitGetValueOrDefault(arg.Type);
966                                 _scope.EmitSet(v);
967                             } else {
968                                 _scope.AddLocal(this, v);
969                                 EmitExpression(arg);
970                                 if (!arg.Type.IsValueType) {
971                                     _ilg.Emit(OpCodes.Dup);
972                                     _ilg.Emit(OpCodes.Ldnull);
973                                     _ilg.Emit(OpCodes.Ceq);
974                                     _ilg.Emit(OpCodes.Stloc, anyNull);
975                                 }
976                                 _scope.EmitSet(v);
977                             }
978                             _ilg.Emit(OpCodes.Ldloc, anyNull);
979                             _ilg.Emit(OpCodes.Brtrue, exitNull);
980                         }
981                         EmitMethodCallExpression(mc);
982                         if (TypeUtils.IsNullableType(resultType) && !TypeUtils.AreEquivalent(resultType, mc.Type)) {
983                             ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type });
984                             _ilg.Emit(OpCodes.Newobj, ci);
985                         }
986                         _ilg.Emit(OpCodes.Br_S, exit);
987                         _ilg.MarkLabel(exitNull);
988                         if (TypeUtils.AreEquivalent(resultType, TypeUtils.GetNullableType(mc.Type))) {
989                             if (resultType.IsValueType) {
990                                 LocalBuilder result = GetLocal(resultType);
991                                 _ilg.Emit(OpCodes.Ldloca, result);
992                                 _ilg.Emit(OpCodes.Initobj, resultType);
993                                 _ilg.Emit(OpCodes.Ldloc, result);
994                                 FreeLocal(result);
995                             } else {
996                                 _ilg.Emit(OpCodes.Ldnull);
997                             }
998                         } else {
999                             switch (nodeType) {
1000                                 case ExpressionType.LessThan:
1001                                 case ExpressionType.LessThanOrEqual:
1002                                 case ExpressionType.GreaterThan:
1003                                 case ExpressionType.GreaterThanOrEqual:
1004                                     _ilg.Emit(OpCodes.Ldc_I4_0);
1005                                     break;
1006                                 default:
1007                                     throw Error.UnknownLiftType(nodeType);
1008                             }
1009                         }
1010                         _ilg.MarkLabel(exit);
1011                         return;
1012                     }
1013                 case ExpressionType.Equal:
1014                 case ExpressionType.NotEqual: {
1015                         if (TypeUtils.AreEquivalent(resultType, TypeUtils.GetNullableType(mc.Type))) {
1016                             goto default;
1017                         }
1018                         Label exit = _ilg.DefineLabel();
1019                         Label exitAllNull = _ilg.DefineLabel();
1020                         Label exitAnyNull = _ilg.DefineLabel();
1021
1022                         LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool));
1023                         LocalBuilder allNull = _ilg.DeclareLocal(typeof(bool));
1024                         _ilg.Emit(OpCodes.Ldc_I4_0);
1025                         _ilg.Emit(OpCodes.Stloc, anyNull);
1026                         _ilg.Emit(OpCodes.Ldc_I4_1);
1027                         _ilg.Emit(OpCodes.Stloc, allNull);
1028
1029                         for (int i = 0, n = paramList.Length; i < n; i++) {
1030                             ParameterExpression v = paramList[i];
1031                             Expression arg = argList[i];
1032                             _scope.AddLocal(this, v);
1033                             if (TypeUtils.IsNullableType(arg.Type)) {
1034                                 EmitAddress(arg, arg.Type);
1035                                 _ilg.Emit(OpCodes.Dup);
1036                                 _ilg.EmitHasValue(arg.Type);
1037                                 _ilg.Emit(OpCodes.Ldc_I4_0);
1038                                 _ilg.Emit(OpCodes.Ceq);
1039                                 _ilg.Emit(OpCodes.Dup);
1040                                 _ilg.Emit(OpCodes.Ldloc, anyNull);
1041                                 _ilg.Emit(OpCodes.Or);
1042                                 _ilg.Emit(OpCodes.Stloc, anyNull);
1043                                 _ilg.Emit(OpCodes.Ldloc, allNull);
1044                                 _ilg.Emit(OpCodes.And);
1045                                 _ilg.Emit(OpCodes.Stloc, allNull);
1046                                 _ilg.EmitGetValueOrDefault(arg.Type);
1047                             } else {
1048                                 EmitExpression(arg);
1049                                 if (!arg.Type.IsValueType) {
1050                                     _ilg.Emit(OpCodes.Dup);
1051                                     _ilg.Emit(OpCodes.Ldnull);
1052                                     _ilg.Emit(OpCodes.Ceq);
1053                                     _ilg.Emit(OpCodes.Dup);
1054                                     _ilg.Emit(OpCodes.Ldloc, anyNull);
1055                                     _ilg.Emit(OpCodes.Or);
1056                                     _ilg.Emit(OpCodes.Stloc, anyNull);
1057                                     _ilg.Emit(OpCodes.Ldloc, allNull);
1058                                     _ilg.Emit(OpCodes.And);
1059                                     _ilg.Emit(OpCodes.Stloc, allNull);
1060                                 } else {
1061                                     _ilg.Emit(OpCodes.Ldc_I4_0);
1062                                     _ilg.Emit(OpCodes.Stloc, allNull);
1063                                 }
1064                             }
1065                             _scope.EmitSet(v);
1066                         }
1067                         _ilg.Emit(OpCodes.Ldloc, allNull);
1068                         _ilg.Emit(OpCodes.Brtrue, exitAllNull);
1069                         _ilg.Emit(OpCodes.Ldloc, anyNull);
1070                         _ilg.Emit(OpCodes.Brtrue, exitAnyNull);
1071
1072                         EmitMethodCallExpression(mc);
1073                         if (TypeUtils.IsNullableType(resultType) && !TypeUtils.AreEquivalent(resultType, mc.Type)) {
1074                             ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type });
1075                             _ilg.Emit(OpCodes.Newobj, ci);
1076                         }
1077                         _ilg.Emit(OpCodes.Br_S, exit);
1078
1079                         _ilg.MarkLabel(exitAllNull);
1080                         _ilg.EmitBoolean(nodeType == ExpressionType.Equal);
1081                         _ilg.Emit(OpCodes.Br_S, exit);
1082
1083                         _ilg.MarkLabel(exitAnyNull);
1084                         _ilg.EmitBoolean(nodeType == ExpressionType.NotEqual);
1085
1086                         _ilg.MarkLabel(exit);
1087                         return;
1088                     }
1089             }
1090         }
1091
1092         #endregion
1093     }
1094 }