1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.Diagnostics;
22 using System.Dynamic.Utils;
24 using Microsoft.Scripting.Utils;
26 using System.Reflection;
27 using System.Reflection.Emit;
28 using System.Runtime.CompilerServices;
30 using Microsoft.Runtime.CompilerServices;
35 namespace System.Linq.Expressions.Compiler {
37 namespace Microsoft.Linq.Expressions.Compiler {
39 partial class LambdaCompiler {
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
50 EmitExpressionStartMask = 0x000f,
51 EmitAsTypeMask = 0x00f0,
52 EmitAsTailCallMask = 0x0f00
56 /// Update the flag with a new EmitAsTailCall flag
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;
65 /// Update the flag with a new EmitExpressionStart flag
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;
74 /// Update the flag with a new EmitAsType flag
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;
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.
87 internal void EmitExpression(Expression node) {
88 EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitExpressionStart);
92 /// Emits an expression and discards the result. For some nodes this emits
93 /// more optimial code then EmitExpression/Pop
95 private void EmitExpressionAsVoid(Expression node) {
96 EmitExpressionAsVoid(node, CompilationFlags.EmitAsNoTail);
99 private void EmitExpressionAsVoid(Expression node, CompilationFlags flags) {
100 Debug.Assert(node != null);
102 CompilationFlags startEmitted = EmitExpressionStart(node);
104 switch (node.NodeType) {
105 case ExpressionType.Assign:
106 EmitAssign((BinaryExpression)node, CompilationFlags.EmitAsVoidType);
108 case ExpressionType.Block:
109 Emit((BlockExpression)node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
111 case ExpressionType.Throw:
112 EmitThrow((UnaryExpression)node, CompilationFlags.EmitAsVoidType);
114 case ExpressionType.Goto:
115 EmitGotoExpression(node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
117 case ExpressionType.Constant:
118 case ExpressionType.Default:
119 case ExpressionType.Parameter:
123 if (node.Type == typeof(void)) {
124 EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitNoExpressionStart));
126 EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
127 _ilg.Emit(OpCodes.Pop);
131 EmitExpressionEnd(startEmitted);
134 private void EmitExpressionAsType(Expression node, Type type, CompilationFlags flags) {
136 if (type == typeof(void)) {
137 EmitExpressionAsVoid(node, flags);
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);
146 // emit the with the flags and emit emit expression start
147 EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart));
152 #region label block tracking
154 private CompilationFlags EmitExpressionStart(Expression node) {
155 if (TryPushLabelBlock(node)) {
156 return CompilationFlags.EmitExpressionStart;
158 return CompilationFlags.EmitNoExpressionStart;
161 private void EmitExpressionEnd(CompilationFlags flags) {
162 if ((flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart) {
163 PopLabelBlock(_labelBlock.Kind);
169 #region InvocationExpression
171 private void EmitInvocationExpression(Expression expr, CompilationFlags flags) {
172 InvocationExpression node = (InvocationExpression)expr;
174 // Optimization: inline code for literal lambda's directly
176 // This is worth it because otherwise we end up with a extra call
177 // to DynamicMethod.CreateDelegate, which is expensive.
179 if (node.LambdaOperand != null) {
180 EmitInlinedInvoke(node, flags);
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]));
189 expr = Expression.Call(expr, expr.Type.GetMethod("Invoke"), node.Arguments);
191 EmitExpression(expr);
194 private void EmitInlinedInvoke(InvocationExpression invoke, CompilationFlags flags) {
195 var lambda = invoke.LambdaOperand;
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.
201 // 1. Emit invoke arguments
202 List<WriteBack> wb = EmitArguments(lambda.Type.GetMethod("Invoke"), invoke);
204 // 2. Create the nested LambdaCompiler
205 var inner = new LambdaCompiler(this, lambda);
208 // if the inlined lambda is the last expression of the whole lambda,
209 // tail call can be applied.
211 flags = UpdateEmitAsTailCallFlag(flags, CompilationFlags.EmitAsNoTail);
213 inner.EmitLambdaBody(_scope, true, flags);
215 // 4. Emit writebacks if needed
221 #region IndexExpression
223 private void EmitIndexExpression(Expression expr) {
224 var node = (IndexExpression)expr;
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);
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) {
238 EmitGetIndexCall(node, objectType);
241 private void EmitIndexAssignment(BinaryExpression node, CompilationFlags flags) {
242 var index = (IndexExpression)node.Left;
244 var emitAs = flags & CompilationFlags.EmitAsTypeMask;
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);
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) {
259 EmitExpression(node.Right);
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));
268 EmitSetIndexCall(index, objectType);
271 if (emitAs != CompilationFlags.EmitAsVoidType) {
272 _ilg.Emit(OpCodes.Ldloc, temp);
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));
286 // For one dimensional arrays, emit load
287 _ilg.EmitLoadElement(node.Type);
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));
300 // For one dimensional arrays, emit store
301 _ilg.EmitStoreElement(node.Type);
307 #region MethodCallExpression
309 private void EmitMethodCallExpression(Expression expr, CompilationFlags flags) {
310 MethodCallExpression node = (MethodCallExpression)expr;
312 EmitMethodCall(node.Object, node.Method, node, flags);
315 private void EmitMethodCallExpression(Expression expr) {
316 EmitMethodCallExpression(expr, CompilationFlags.EmitAsNoTail);
319 private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr) {
320 EmitMethodCall(obj, method, methodCallExpr, CompilationFlags.EmitAsNoTail);
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);
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);
334 EmitMethodCall(method, methodCallExpr, objectType, flags);
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);
343 // assumes 'object' of non-static call is already on stack
344 private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType, CompilationFlags flags) {
347 List<WriteBack> wb = EmitArguments(mi, args);
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);
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);
363 if (mi.CallingConvention == CallingConventions.VarArgs) {
364 _ilg.EmitCall(callOp, mi, args.Map(a => a.Type));
366 _ilg.Emit(callOp, mi);
369 // Emit writebacks for properties passed as "ref" arguments
373 private static bool MethodHasByRefParameter(MethodInfo mi) {
374 foreach (var pi in mi.GetParametersCached()) {
375 if (pi.IsByRefParameter()) {
382 private void EmitCall(Type objectType, MethodInfo method) {
383 if (method.CallingConvention == CallingConventions.VarArgs) {
384 throw Error.UnexpectedVarArgsCall(method);
387 OpCode callOp = UseVirtual(method) ? OpCodes.Callvirt : OpCodes.Call;
388 if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
389 _ilg.Emit(OpCodes.Constrained, objectType);
391 _ilg.Emit(callOp, method);
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?
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
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.
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.
418 if (mi.DeclaringType.IsValueType) {
425 /// Emits arguments to a call, and returns an array of writebacks that
426 /// should happen after the call.
428 private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args) {
429 return EmitArguments(method, args, 0);
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).
437 private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args, int skipParameters) {
438 ParameterInfo[] pis = method.GetParametersCached();
439 Debug.Assert(args.ArgumentCount + skipParameters == pis.Length);
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;
448 type = type.GetElementType();
450 WriteBack wb = EmitAddressWriteBack(argument, type);
455 EmitExpression(argument);
461 private static void EmitWriteBack(IList<WriteBack> writeBacks) {
462 foreach (WriteBack wb in writeBacks) {
469 private void EmitConstantExpression(Expression expr) {
470 ConstantExpression node = (ConstantExpression)expr;
472 EmitConstant(node.Value, node.Type);
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);
482 _boundConstants.EmitConstant(this, value, type);
485 private void EmitDynamicExpression(Expression expr) {
486 if (!(_method is DynamicMethod)) {
487 throw Error.CannotCompileDynamic();
490 var node = (DynamicExpression)expr;
492 var site = CallSite.Create(node.DelegateType, node.Binder);
493 Type siteType = site.GetType();
495 var invoke = node.DelegateType.GetMethod("Invoke");
497 // site.Target.Invoke(site, args)
498 EmitConstant(site, siteType);
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);
507 var siteTemp = GetLocal(typeof(CallSite));
509 _ilg.Emit(OpCodes.Stloc, siteTemp);
510 _ilg.Emit(OpCodes.Ldfld, siteType.GetField("Target"));
511 _ilg.Emit(OpCodes.Ldloc, siteTemp);
514 List<WriteBack> wb = EmitArguments(invoke, node, 1);
515 _ilg.Emit(OpCodes.Callvirt, invoke);
519 private void EmitNewExpression(Expression expr) {
520 NewExpression node = (NewExpression)expr;
522 if (node.Constructor != null) {
523 List<WriteBack> wb = EmitArguments(node.Constructor, node);
524 _ilg.Emit(OpCodes.Newobj, node.Constructor);
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);
537 private void EmitTypeBinaryExpression(Expression expr) {
538 TypeBinaryExpression node = (TypeBinaryExpression)expr;
540 if (node.NodeType == ExpressionType.TypeEqual) {
541 EmitExpression(node.ReduceTypeEqual());
545 Type type = node.Expression.Type;
547 // Try to determine the result statically
548 AnalyzeTypeIsResult result = ConstantCheck.AnalyzeTypeIs(node);
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);
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);
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);
577 Debug.Assert(result == AnalyzeTypeIsResult.Unknown);
579 // Emit a full runtime "isinst" check
580 EmitExpression(node.Expression);
581 if (type.IsValueType) {
582 _ilg.Emit(OpCodes.Box, type);
584 _ilg.Emit(OpCodes.Isinst, node.TypeOperand);
585 _ilg.Emit(OpCodes.Ldnull);
586 _ilg.Emit(OpCodes.Cgt_Un);
589 private void EmitVariableAssignment(BinaryExpression node, CompilationFlags flags) {
590 var variable = (ParameterExpression)node.Left;
591 var emitAs = flags & CompilationFlags.EmitAsTypeMask;
593 EmitExpression(node.Right);
594 if (emitAs != CompilationFlags.EmitAsVoidType) {
595 _ilg.Emit(OpCodes.Dup);
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
604 LocalBuilder value = GetLocal(variable.Type);
605 _ilg.Emit(OpCodes.Stloc, value);
606 _scope.EmitGet(variable);
607 _ilg.Emit(OpCodes.Ldloc, value);
609 _ilg.EmitStoreValueIndirect(variable.Type);
611 _scope.EmitSet(variable);
615 private void EmitAssignBinaryExpression(Expression expr) {
616 EmitAssign((BinaryExpression)expr, CompilationFlags.EmitAsDefaultType);
619 private void EmitAssign(BinaryExpression node, CompilationFlags emitAs) {
620 switch (node.Left.NodeType) {
621 case ExpressionType.Index:
622 EmitIndexAssignment(node, emitAs);
624 case ExpressionType.MemberAccess:
625 EmitMemberAssignment(node, emitAs);
627 case ExpressionType.Parameter:
628 EmitVariableAssignment(node, emitAs);
631 throw Error.InvalidLvalue(node.Left.NodeType);
635 private void EmitParameterExpression(Expression expr) {
636 ParameterExpression node = (ParameterExpression)expr;
637 _scope.EmitGet(node);
639 _ilg.EmitLoadValueIndirect(node.Type);
643 private void EmitLambdaExpression(Expression expr) {
644 LambdaExpression node = (LambdaExpression)expr;
645 EmitDelegateConstruction(node);
648 private void EmitRuntimeVariablesExpression(Expression expr) {
649 RuntimeVariablesExpression node = (RuntimeVariablesExpression)expr;
650 _scope.EmitVariableAccess(this, node.Variables);
653 private void EmitMemberAssignment(BinaryExpression node, CompilationFlags flags) {
654 MemberExpression lvalue = (MemberExpression)node.Left;
655 MemberInfo member = lvalue.Member;
657 // emit "this", if any
658 Type objectType = null;
659 if (lvalue.Expression != null) {
660 EmitInstance(lvalue.Expression, objectType = lvalue.Expression.Type);
664 EmitExpression(node.Right);
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));
674 switch (member.MemberType) {
675 case MemberTypes.Field:
676 _ilg.EmitFieldSet((FieldInfo)member);
678 case MemberTypes.Property:
679 EmitCall(objectType, ((PropertyInfo)member).GetSetMethod(true));
682 throw Error.InvalidMemberType(member.MemberType);
685 if (emitAs != CompilationFlags.EmitAsVoidType) {
686 _ilg.Emit(OpCodes.Ldloc, temp);
691 private void EmitMemberExpression(Expression expr) {
692 MemberExpression node = (MemberExpression)expr;
694 // emit "this", if any
695 Type instanceType = null;
696 if (node.Expression != null) {
697 EmitInstance(node.Expression, instanceType = node.Expression.Type);
700 EmitMemberGet(node.Member, instanceType);
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;
709 EmitConstant(fi.GetRawConstantValue(), fi.FieldType);
711 _ilg.EmitFieldGet(fi);
714 case MemberTypes.Property:
715 EmitCall(objectType, ((PropertyInfo)member).GetGetMethod(true));
718 throw ContractUtils.Unreachable;
722 private void EmitInstance(Expression instance, Type type) {
723 if (instance != null) {
724 if (type.IsValueType) {
725 EmitAddress(instance, type);
727 EmitExpression(instance);
732 private void EmitNewArrayExpression(Expression expr) {
733 NewArrayExpression node = (NewArrayExpression)expr;
735 if (node.NodeType == ExpressionType.NewArrayInit) {
737 node.Type.GetElementType(),
738 node.Expressions.Count,
739 delegate(int index) {
740 EmitExpression(node.Expressions[index]);
744 ReadOnlyCollection<Expression> bounds = node.Expressions;
745 for (int i = 0; i < bounds.Count; i++) {
746 Expression x = bounds[i];
748 _ilg.EmitConvertToType(x.Type, typeof(int), true);
750 _ilg.EmitArray(node.Type);
754 private void EmitDebugInfoExpression(Expression expr) {
755 if (!EmitDebugSymbols) {
758 var node = (DebugInfoExpression)expr;
760 if (node.IsClear && _sequencePointCleared) {
761 // Emitting another clearance after one clearance does not
762 // have any effect, so we can save it.
766 _tree.DebugInfoGenerator.MarkSequencePoint(_lambda, _method, _ilg, node);
767 _ilg.Emit(OpCodes.Nop);
768 _sequencePointCleared = node.IsClear;
771 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expr")]
772 private static void EmitExtensionExpression(Expression expr) {
773 throw Error.ExtensionNotReduced();
776 #region ListInit, MemberInit
778 private void EmitListInitExpression(Expression expr) {
779 EmitListInit((ListInitExpression)expr);
782 private void EmitMemberInitExpression(Expression expr) {
783 EmitMemberInit((MemberInitExpression)expr);
786 private void EmitBinding(MemberBinding binding, Type objectType) {
787 switch (binding.BindingType) {
788 case MemberBindingType.Assignment:
789 EmitMemberAssignment((MemberAssignment)binding, objectType);
791 case MemberBindingType.ListBinding:
792 EmitMemberListBinding((MemberListBinding)binding);
794 case MemberBindingType.MemberBinding:
795 EmitMemberMemberBinding((MemberMemberBinding)binding);
798 throw Error.UnknownBindingType();
802 private void EmitMemberAssignment(MemberAssignment binding, Type objectType) {
803 EmitExpression(binding.Expression);
804 FieldInfo fi = binding.Member as FieldInfo;
806 _ilg.Emit(OpCodes.Stfld, fi);
808 PropertyInfo pi = binding.Member as PropertyInfo;
810 EmitCall(objectType, pi.GetSetMethod(true));
812 throw Error.UnhandledBinding();
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);
822 if (type.IsValueType) {
823 EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
825 EmitMemberGet(binding.Member, binding.Member.DeclaringType);
827 EmitMemberInit(binding.Bindings, false, type);
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);
835 if (type.IsValueType) {
836 EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
838 EmitMemberGet(binding.Member, binding.Member.DeclaringType);
840 EmitListInit(binding.Initializers, false, type);
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);
851 EmitMemberInit(init.Bindings, loc == null, init.NewExpression.Type);
853 _ilg.Emit(OpCodes.Ldloc, loc);
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;
862 // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
864 _ilg.Emit(OpCodes.Pop);
867 for (int i = 0; i < n; i++) {
868 if (keepOnStack || i < n - 1) {
869 _ilg.Emit(OpCodes.Dup);
871 EmitBinding(bindings[i], objectType);
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);
884 EmitListInit(init.Initializers, loc == null, init.NewExpression.Type);
886 _ilg.Emit(OpCodes.Ldloc, loc);
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;
896 // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
898 _ilg.Emit(OpCodes.Pop);
901 for (int i = 0; i < n; i++) {
902 if (keepOnStack || i < n - 1) {
903 _ilg.Emit(OpCodes.Dup);
905 EmitMethodCall(initializers[i].AddMethod, initializers[i], objectType);
907 // Aome add methods, ArrayList.Add for example, return non-void
908 if (initializers[i].AddMethod.ReturnType != typeof(void)) {
909 _ilg.Emit(OpCodes.Pop);
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);
925 #region Expression helpers
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);
931 if (variables.Count != arguments.Count) {
932 throw Error.IncorrectNumberOfIndexes();
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();
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)));
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);
968 _scope.AddLocal(this, v);
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);
978 _ilg.Emit(OpCodes.Ldloc, anyNull);
979 _ilg.Emit(OpCodes.Brtrue, exitNull);
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);
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);
996 _ilg.Emit(OpCodes.Ldnull);
1000 case ExpressionType.LessThan:
1001 case ExpressionType.LessThanOrEqual:
1002 case ExpressionType.GreaterThan:
1003 case ExpressionType.GreaterThanOrEqual:
1004 _ilg.Emit(OpCodes.Ldc_I4_0);
1007 throw Error.UnknownLiftType(nodeType);
1010 _ilg.MarkLabel(exit);
1013 case ExpressionType.Equal:
1014 case ExpressionType.NotEqual: {
1015 if (TypeUtils.AreEquivalent(resultType, TypeUtils.GetNullableType(mc.Type))) {
1018 Label exit = _ilg.DefineLabel();
1019 Label exitAllNull = _ilg.DefineLabel();
1020 Label exitAnyNull = _ilg.DefineLabel();
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);
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);
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);
1061 _ilg.Emit(OpCodes.Ldc_I4_0);
1062 _ilg.Emit(OpCodes.Stloc, allNull);
1067 _ilg.Emit(OpCodes.Ldloc, allNull);
1068 _ilg.Emit(OpCodes.Brtrue, exitAllNull);
1069 _ilg.Emit(OpCodes.Ldloc, anyNull);
1070 _ilg.Emit(OpCodes.Brtrue, exitAnyNull);
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);
1077 _ilg.Emit(OpCodes.Br_S, exit);
1079 _ilg.MarkLabel(exitAllNull);
1080 _ilg.EmitBoolean(nodeType == ExpressionType.Equal);
1081 _ilg.Emit(OpCodes.Br_S, exit);
1083 _ilg.MarkLabel(exitAnyNull);
1084 _ilg.EmitBoolean(nodeType == ExpressionType.NotEqual);
1086 _ilg.MarkLabel(exit);