1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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 Apache License, Version 2.0, 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 Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Reflection;
22 using System.Reflection.Emit;
23 using System.Runtime.CompilerServices;
26 namespace Microsoft.Scripting.Ast.Compiler {
28 namespace System.Linq.Expressions.Compiler {
30 partial class LambdaCompiler {
32 internal enum CompilationFlags {
33 EmitExpressionStart = 0x0001,
34 EmitNoExpressionStart = 0x0002,
35 EmitAsDefaultType = 0x0010,
36 EmitAsVoidType = 0x0020,
37 EmitAsTail = 0x0100, // at the tail position of a lambda, tail call can be safely emitted
38 EmitAsMiddle = 0x0200, // in the middle of a lambda, tail call can be emitted if it is in a return
39 EmitAsNoTail = 0x0400, // neither at the tail or in a return, or tail call is not turned on, no tail call is emitted
41 EmitExpressionStartMask = 0x000f,
42 EmitAsTypeMask = 0x00f0,
43 EmitAsTailCallMask = 0x0f00
47 /// Update the flag with a new EmitAsTailCall flag
49 private static CompilationFlags UpdateEmitAsTailCallFlag(CompilationFlags flags, CompilationFlags newValue) {
50 Debug.Assert(newValue == CompilationFlags.EmitAsTail || newValue == CompilationFlags.EmitAsMiddle || newValue == CompilationFlags.EmitAsNoTail);
51 var oldValue = flags & CompilationFlags.EmitAsTailCallMask;
52 return flags ^ oldValue | newValue;
56 /// Update the flag with a new EmitExpressionStart flag
58 private static CompilationFlags UpdateEmitExpressionStartFlag(CompilationFlags flags, CompilationFlags newValue) {
59 Debug.Assert(newValue == CompilationFlags.EmitExpressionStart || newValue == CompilationFlags.EmitNoExpressionStart);
60 var oldValue = flags & CompilationFlags.EmitExpressionStartMask;
61 return flags ^ oldValue | newValue;
65 /// Update the flag with a new EmitAsType flag
67 private static CompilationFlags UpdateEmitAsTypeFlag(CompilationFlags flags, CompilationFlags newValue) {
68 Debug.Assert(newValue == CompilationFlags.EmitAsDefaultType || newValue == CompilationFlags.EmitAsVoidType);
69 var oldValue = flags & CompilationFlags.EmitAsTypeMask;
70 return flags ^ oldValue | newValue;
74 /// Generates code for this expression in a value position.
75 /// This method will leave the value of the expression
76 /// on the top of the stack typed as Type.
78 internal void EmitExpression(Expression node) {
79 EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitExpressionStart);
83 /// Emits an expression and discards the result. For some nodes this emits
84 /// more optimial code then EmitExpression/Pop
86 private void EmitExpressionAsVoid(Expression node) {
87 EmitExpressionAsVoid(node, CompilationFlags.EmitAsNoTail);
90 private void EmitExpressionAsVoid(Expression node, CompilationFlags flags) {
91 Debug.Assert(node != null);
93 CompilationFlags startEmitted = EmitExpressionStart(node);
95 switch (node.NodeType) {
96 case ExpressionType.Assign:
97 EmitAssign((BinaryExpression)node, CompilationFlags.EmitAsVoidType);
99 case ExpressionType.Block:
100 Emit((BlockExpression)node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
102 case ExpressionType.Throw:
103 EmitThrow((UnaryExpression)node, CompilationFlags.EmitAsVoidType);
105 case ExpressionType.Goto:
106 EmitGotoExpression(node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
108 case ExpressionType.Constant:
109 case ExpressionType.Default:
110 case ExpressionType.Parameter:
114 if (node.Type == typeof(void)) {
115 EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitNoExpressionStart));
117 EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
118 _ilg.Emit(OpCodes.Pop);
122 EmitExpressionEnd(startEmitted);
125 private void EmitExpressionAsType(Expression node, Type type, CompilationFlags flags) {
127 if (type == typeof(void)) {
128 EmitExpressionAsVoid(node, flags);
130 // if the node is emitted as a different type, CastClass IL is emitted at the end,
131 // should not emit with tail calls.
132 if (!TypeUtils.AreEquivalent(node.Type, type)) {
133 EmitExpression(node);
134 Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type));
135 _ilg.Emit(OpCodes.Castclass, type);
137 // emit the with the flags and emit emit expression start
138 EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart));
143 #region label block tracking
145 private CompilationFlags EmitExpressionStart(Expression node) {
146 if (TryPushLabelBlock(node)) {
147 return CompilationFlags.EmitExpressionStart;
149 return CompilationFlags.EmitNoExpressionStart;
152 private void EmitExpressionEnd(CompilationFlags flags) {
153 if ((flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart) {
154 PopLabelBlock(_labelBlock.Kind);
160 #region InvocationExpression
162 private void EmitInvocationExpression(Expression expr, CompilationFlags flags) {
163 InvocationExpression node = (InvocationExpression)expr;
165 // Optimization: inline code for literal lambda's directly
167 // This is worth it because otherwise we end up with a extra call
168 // to DynamicMethod.CreateDelegate, which is expensive.
170 if (node.LambdaOperand != null) {
171 EmitInlinedInvoke(node, flags);
175 expr = node.Expression;
176 if (typeof(LambdaExpression).IsAssignableFrom(expr.Type)) {
177 // if the invoke target is a lambda expression tree, first compile it into a delegate
178 expr = Expression.Call(expr, expr.Type.GetMethod("Compile", new Type[0]));
180 expr = Expression.Call(expr, expr.Type.GetMethod("Invoke"), node.Arguments);
182 EmitExpression(expr);
185 private void EmitInlinedInvoke(InvocationExpression invoke, CompilationFlags flags) {
186 var lambda = invoke.LambdaOperand;
188 // This is tricky: we need to emit the arguments outside of the
189 // scope, but set them inside the scope. Fortunately, using the IL
190 // stack it is entirely doable.
192 // 1. Emit invoke arguments
193 List<WriteBack> wb = EmitArguments(lambda.Type.GetMethod("Invoke"), invoke);
195 // 2. Create the nested LambdaCompiler
196 var inner = new LambdaCompiler(this, lambda);
199 // if the inlined lambda is the last expression of the whole lambda,
200 // tail call can be applied.
202 flags = UpdateEmitAsTailCallFlag(flags, CompilationFlags.EmitAsNoTail);
204 inner.EmitLambdaBody(_scope, true, flags);
206 // 4. Emit writebacks if needed
212 #region IndexExpression
214 private void EmitIndexExpression(Expression expr) {
215 var node = (IndexExpression)expr;
217 // Emit instance, if calling an instance method
218 Type objectType = null;
219 if (node.Object != null) {
220 EmitInstance(node.Object, objectType = node.Object.Type);
223 // Emit indexes. We don't allow byref args, so no need to worry
224 // about writebacks or EmitAddress
225 foreach (var arg in node.Arguments) {
229 EmitGetIndexCall(node, objectType);
232 private void EmitIndexAssignment(BinaryExpression node, CompilationFlags flags) {
233 var index = (IndexExpression)node.Left;
235 var emitAs = flags & CompilationFlags.EmitAsTypeMask;
237 // Emit instance, if calling an instance method
238 Type objectType = null;
239 if (index.Object != null) {
240 EmitInstance(index.Object, objectType = index.Object.Type);
243 // Emit indexes. We don't allow byref args, so no need to worry
244 // about writebacks or EmitAddress
245 foreach (var arg in index.Arguments) {
250 EmitExpression(node.Right);
252 // Save the expression value, if needed
253 LocalBuilder temp = null;
254 if (emitAs != CompilationFlags.EmitAsVoidType) {
255 _ilg.Emit(OpCodes.Dup);
256 _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type));
259 EmitSetIndexCall(index, objectType);
262 if (emitAs != CompilationFlags.EmitAsVoidType) {
263 _ilg.Emit(OpCodes.Ldloc, temp);
268 private void EmitGetIndexCall(IndexExpression node, Type objectType) {
269 if (node.Indexer != null) {
270 // For indexed properties, just call the getter
271 var method = node.Indexer.GetGetMethod(true);
272 EmitCall(objectType, method);
273 } else if (node.Arguments.Count != 1) {
274 // Multidimensional arrays, call get
275 _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance));
277 // For one dimensional arrays, emit load
278 _ilg.EmitLoadElement(node.Type);
282 private void EmitSetIndexCall(IndexExpression node, Type objectType) {
283 if (node.Indexer != null) {
284 // For indexed properties, just call the setter
285 var method = node.Indexer.GetSetMethod(true);
286 EmitCall(objectType, method);
287 } else if (node.Arguments.Count != 1) {
288 // Multidimensional arrays, call set
289 _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance));
291 // For one dimensional arrays, emit store
292 _ilg.EmitStoreElement(node.Type);
298 #region MethodCallExpression
300 private void EmitMethodCallExpression(Expression expr, CompilationFlags flags) {
301 MethodCallExpression node = (MethodCallExpression)expr;
303 EmitMethodCall(node.Object, node.Method, node, flags);
306 private void EmitMethodCallExpression(Expression expr) {
307 EmitMethodCallExpression(expr, CompilationFlags.EmitAsNoTail);
310 private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr) {
311 EmitMethodCall(obj, method, methodCallExpr, CompilationFlags.EmitAsNoTail);
314 private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr, CompilationFlags flags) {
315 // Emit instance, if calling an instance method
316 Type objectType = null;
317 if (!method.IsStatic) {
318 EmitInstance(obj, objectType = obj.Type);
320 // if the obj has a value type, its address is passed to the method call so we cannot destroy the
321 // stack by emitting a tail call
322 if (obj != null && obj.Type.IsValueType) {
323 EmitMethodCall(method, methodCallExpr, objectType);
325 EmitMethodCall(method, methodCallExpr, objectType, flags);
329 // assumes 'object' of non-static call is already on stack
330 private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType) {
331 EmitMethodCall(mi, args, objectType, CompilationFlags.EmitAsNoTail);
334 // assumes 'object' of non-static call is already on stack
335 private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType, CompilationFlags flags) {
338 List<WriteBack> wb = EmitArguments(mi, args);
340 // Emit the actual call
341 OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call;
342 if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
343 // This automatically boxes value types if necessary.
344 _ilg.Emit(OpCodes.Constrained, objectType);
346 // The method call can be a tail call if
347 // 1) the method call is the last instruction before Ret
348 // 2) the method does not have any ByRef parameters, refer to ECMA-335 Partition III Section 2.4.
349 // "Verification requires that no managed pointers are passed to the method being called, since
350 // it does not track pointers into the current frame."
351 if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail && !MethodHasByRefParameter(mi)) {
352 _ilg.Emit(OpCodes.Tailcall);
354 if (mi.CallingConvention == CallingConventions.VarArgs) {
355 _ilg.EmitCall(callOp, mi, args.Map(a => a.Type));
357 _ilg.Emit(callOp, mi);
360 // Emit writebacks for properties passed as "ref" arguments
364 private static bool MethodHasByRefParameter(MethodInfo mi) {
365 foreach (var pi in mi.GetParametersCached()) {
366 if (pi.IsByRefParameter()) {
373 private void EmitCall(Type objectType, MethodInfo method) {
374 if (method.CallingConvention == CallingConventions.VarArgs) {
375 throw Error.UnexpectedVarArgsCall(method);
378 OpCode callOp = UseVirtual(method) ? OpCodes.Callvirt : OpCodes.Call;
379 if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
380 _ilg.Emit(OpCodes.Constrained, objectType);
382 _ilg.Emit(callOp, method);
385 private static bool UseVirtual(MethodInfo mi) {
386 // There are two factors: is the method static, virtual or non-virtual instance?
387 // And is the object ref or value?
391 // static, value: call
392 // virtual, ref: callvirt
393 // virtual, value: call -- eg, double.ToString must be a non-virtual call to be verifiable.
394 // instance, ref: callvirt -- this looks wrong, but is verifiable and gives us a free null check.
395 // instance, value: call
397 // We never need to generate a nonvirtual call to a virtual method on a reference type because
398 // expression trees do not support "base.Foo()" style calling.
400 // We could do an optimization here for the case where we know that the object is a non-null
401 // reference type and the method is a non-virtual instance method. For example, if we had
402 // (new Foo()).Bar() for instance method Bar we don't need the null check so we could do a
403 // call rather than a callvirt. However that seems like it would not be a very big win for
404 // most dynamically generated code scenarios, so let's not do that for now.
409 if (mi.DeclaringType.IsValueType) {
416 /// Emits arguments to a call, and returns an array of writebacks that
417 /// should happen after the call.
419 private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args) {
420 return EmitArguments(method, args, 0);
424 /// Emits arguments to a call, and returns an array of writebacks that
425 /// should happen after the call. For emitting dynamic expressions, we
426 /// need to skip the first parameter of the method (the call site).
428 private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args, int skipParameters) {
429 ParameterInfo[] pis = method.GetParametersCached();
430 Debug.Assert(args.ArgumentCount + skipParameters == pis.Length);
432 var writeBacks = new List<WriteBack>();
433 for (int i = skipParameters, n = pis.Length; i < n; i++) {
434 ParameterInfo parameter = pis[i];
435 Expression argument = args.GetArgument(i - skipParameters);
436 Type type = parameter.ParameterType;
439 type = type.GetElementType();
441 WriteBack wb = EmitAddressWriteBack(argument, type);
446 EmitExpression(argument);
452 private static void EmitWriteBack(IList<WriteBack> writeBacks) {
453 foreach (WriteBack wb in writeBacks) {
460 private void EmitConstantExpression(Expression expr) {
461 ConstantExpression node = (ConstantExpression)expr;
463 EmitConstant(node.Value, node.Type);
466 private void EmitConstant(object value, Type type) {
467 // Try to emit the constant directly into IL
468 if (ILGen.CanEmitConstant(value, type)) {
469 _ilg.EmitConstant(value, type);
473 _boundConstants.EmitConstant(this, value, type);
476 private void EmitDynamicExpression(Expression expr) {
477 if (!(_method is DynamicMethod)) {
478 throw Error.CannotCompileDynamic();
481 var node = (DynamicExpression)expr;
483 var site = CallSite.Create(node.DelegateType, node.Binder);
484 Type siteType = site.GetType();
486 var invoke = node.DelegateType.GetMethod("Invoke");
488 // site.Target.Invoke(site, args)
489 EmitConstant(site, siteType);
491 // Emit the temp as type CallSite so we get more reuse
492 _ilg.Emit(OpCodes.Dup);
493 #if !FEATURE_CORE_DLR
494 // For 3.5, emit the temp as CallSite<T> to work around a Jit32
495 // verifier issue (fixed in 3.5 sp1)
496 var siteTemp = GetLocal(siteType);
498 var siteTemp = GetLocal(typeof(CallSite));
500 _ilg.Emit(OpCodes.Stloc, siteTemp);
501 _ilg.Emit(OpCodes.Ldfld, siteType.GetField("Target"));
502 _ilg.Emit(OpCodes.Ldloc, siteTemp);
505 List<WriteBack> wb = EmitArguments(invoke, node, 1);
506 _ilg.Emit(OpCodes.Callvirt, invoke);
510 private void EmitNewExpression(Expression expr) {
511 NewExpression node = (NewExpression)expr;
513 if (node.Constructor != null) {
514 List<WriteBack> wb = EmitArguments(node.Constructor, node);
515 _ilg.Emit(OpCodes.Newobj, node.Constructor);
518 Debug.Assert(node.Arguments.Count == 0, "Node with arguments must have a constructor.");
519 Debug.Assert(node.Type.IsValueType, "Only value type may have constructor not set.");
520 LocalBuilder temp = GetLocal(node.Type);
521 _ilg.Emit(OpCodes.Ldloca, temp);
522 _ilg.Emit(OpCodes.Initobj, node.Type);
523 _ilg.Emit(OpCodes.Ldloc, temp);
528 private void EmitTypeBinaryExpression(Expression expr) {
529 TypeBinaryExpression node = (TypeBinaryExpression)expr;
531 if (node.NodeType == ExpressionType.TypeEqual) {
532 EmitExpression(node.ReduceTypeEqual());
536 Type type = node.Expression.Type;
538 // Try to determine the result statically
539 AnalyzeTypeIsResult result = ConstantCheck.AnalyzeTypeIs(node);
541 if (result == AnalyzeTypeIsResult.KnownTrue ||
542 result == AnalyzeTypeIsResult.KnownFalse) {
543 // Result is known statically, so just emit the expression for
544 // its side effects and return the result
545 EmitExpressionAsVoid(node.Expression);
546 _ilg.EmitBoolean(result == AnalyzeTypeIsResult.KnownTrue);
550 if (result == AnalyzeTypeIsResult.KnownAssignable) {
551 // We know the type can be assigned, but still need to check
552 // for null at runtime
553 if (type.IsNullableType()) {
554 EmitAddress(node.Expression, type);
555 _ilg.EmitHasValue(type);
559 Debug.Assert(!type.IsValueType);
560 EmitExpression(node.Expression);
561 _ilg.Emit(OpCodes.Ldnull);
562 _ilg.Emit(OpCodes.Ceq);
563 _ilg.Emit(OpCodes.Ldc_I4_0);
564 _ilg.Emit(OpCodes.Ceq);
568 Debug.Assert(result == AnalyzeTypeIsResult.Unknown);
570 // Emit a full runtime "isinst" check
571 EmitExpression(node.Expression);
572 if (type.IsValueType) {
573 _ilg.Emit(OpCodes.Box, type);
575 _ilg.Emit(OpCodes.Isinst, node.TypeOperand);
576 _ilg.Emit(OpCodes.Ldnull);
577 _ilg.Emit(OpCodes.Cgt_Un);
580 private void EmitVariableAssignment(BinaryExpression node, CompilationFlags flags) {
581 var variable = (ParameterExpression)node.Left;
582 var emitAs = flags & CompilationFlags.EmitAsTypeMask;
584 EmitExpression(node.Right);
585 if (emitAs != CompilationFlags.EmitAsVoidType) {
586 _ilg.Emit(OpCodes.Dup);
589 if (variable.IsByRef) {
590 // Note: the stloc/ldloc pattern is a bit suboptimal, but it
591 // saves us from having to spill stack when assigning to a
592 // byref parameter. We already make this same tradeoff for
593 // hoisted variables, see ElementStorage.EmitStore
595 LocalBuilder value = GetLocal(variable.Type);
596 _ilg.Emit(OpCodes.Stloc, value);
597 _scope.EmitGet(variable);
598 _ilg.Emit(OpCodes.Ldloc, value);
600 _ilg.EmitStoreValueIndirect(variable.Type);
602 _scope.EmitSet(variable);
606 private void EmitAssignBinaryExpression(Expression expr) {
607 EmitAssign((BinaryExpression)expr, CompilationFlags.EmitAsDefaultType);
610 private void EmitAssign(BinaryExpression node, CompilationFlags emitAs) {
611 switch (node.Left.NodeType) {
612 case ExpressionType.Index:
613 EmitIndexAssignment(node, emitAs);
615 case ExpressionType.MemberAccess:
616 EmitMemberAssignment(node, emitAs);
618 case ExpressionType.Parameter:
619 EmitVariableAssignment(node, emitAs);
622 throw Error.InvalidLvalue(node.Left.NodeType);
626 private void EmitParameterExpression(Expression expr) {
627 ParameterExpression node = (ParameterExpression)expr;
628 _scope.EmitGet(node);
630 _ilg.EmitLoadValueIndirect(node.Type);
634 private void EmitLambdaExpression(Expression expr) {
635 LambdaExpression node = (LambdaExpression)expr;
636 EmitDelegateConstruction(node);
639 private void EmitRuntimeVariablesExpression(Expression expr) {
640 RuntimeVariablesExpression node = (RuntimeVariablesExpression)expr;
641 _scope.EmitVariableAccess(this, node.Variables);
644 private void EmitMemberAssignment(BinaryExpression node, CompilationFlags flags) {
645 MemberExpression lvalue = (MemberExpression)node.Left;
646 MemberInfo member = lvalue.Member;
648 // emit "this", if any
649 Type objectType = null;
650 if (lvalue.Expression != null) {
651 EmitInstance(lvalue.Expression, objectType = lvalue.Expression.Type);
655 EmitExpression(node.Right);
657 LocalBuilder temp = null;
658 var emitAs = flags & CompilationFlags.EmitAsTypeMask;
659 if (emitAs != CompilationFlags.EmitAsVoidType) {
660 // save the value so we can return it
661 _ilg.Emit(OpCodes.Dup);
662 _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type));
665 switch (member.MemberType) {
666 case MemberTypes.Field:
667 _ilg.EmitFieldSet((FieldInfo)member);
669 case MemberTypes.Property:
670 EmitCall(objectType, ((PropertyInfo)member).GetSetMethod(true));
673 throw Error.InvalidMemberType(member.MemberType);
676 if (emitAs != CompilationFlags.EmitAsVoidType) {
677 _ilg.Emit(OpCodes.Ldloc, temp);
682 private void EmitMemberExpression(Expression expr) {
683 MemberExpression node = (MemberExpression)expr;
685 // emit "this", if any
686 Type instanceType = null;
687 if (node.Expression != null) {
688 EmitInstance(node.Expression, instanceType = node.Expression.Type);
691 EmitMemberGet(node.Member, instanceType);
694 // assumes instance is already on the stack
695 private void EmitMemberGet(MemberInfo member, Type objectType) {
696 switch (member.MemberType) {
697 case MemberTypes.Field:
698 FieldInfo fi = (FieldInfo)member;
700 EmitConstant(fi.GetRawConstantValue(), fi.FieldType);
702 _ilg.EmitFieldGet(fi);
705 case MemberTypes.Property:
706 EmitCall(objectType, ((PropertyInfo)member).GetGetMethod(true));
709 throw ContractUtils.Unreachable;
713 private void EmitInstance(Expression instance, Type type) {
714 if (instance != null) {
715 if (type.IsValueType) {
716 EmitAddress(instance, type);
718 EmitExpression(instance);
723 private void EmitNewArrayExpression(Expression expr) {
724 NewArrayExpression node = (NewArrayExpression)expr;
726 if (node.NodeType == ExpressionType.NewArrayInit) {
728 node.Type.GetElementType(),
729 node.Expressions.Count,
730 delegate(int index) {
731 EmitExpression(node.Expressions[index]);
735 ReadOnlyCollection<Expression> bounds = node.Expressions;
736 for (int i = 0; i < bounds.Count; i++) {
737 Expression x = bounds[i];
739 _ilg.EmitConvertToType(x.Type, typeof(int), true);
741 _ilg.EmitArray(node.Type);
745 private void EmitDebugInfoExpression(Expression expr) {
746 if (!EmitDebugSymbols) {
749 var node = (DebugInfoExpression)expr;
751 if (node.IsClear && _sequencePointCleared) {
752 // Emitting another clearance after one clearance does not
753 // have any effect, so we can save it.
757 _tree.DebugInfoGenerator.MarkSequencePoint(_lambda, _method, _ilg, node);
758 _ilg.Emit(OpCodes.Nop);
759 _sequencePointCleared = node.IsClear;
762 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expr")]
763 private static void EmitExtensionExpression(Expression expr) {
764 throw Error.ExtensionNotReduced();
767 #region ListInit, MemberInit
769 private void EmitListInitExpression(Expression expr) {
770 EmitListInit((ListInitExpression)expr);
773 private void EmitMemberInitExpression(Expression expr) {
774 EmitMemberInit((MemberInitExpression)expr);
777 private void EmitBinding(MemberBinding binding, Type objectType) {
778 switch (binding.BindingType) {
779 case MemberBindingType.Assignment:
780 EmitMemberAssignment((MemberAssignment)binding, objectType);
782 case MemberBindingType.ListBinding:
783 EmitMemberListBinding((MemberListBinding)binding);
785 case MemberBindingType.MemberBinding:
786 EmitMemberMemberBinding((MemberMemberBinding)binding);
789 throw Error.UnknownBindingType();
793 private void EmitMemberAssignment(MemberAssignment binding, Type objectType) {
794 EmitExpression(binding.Expression);
795 FieldInfo fi = binding.Member as FieldInfo;
797 _ilg.Emit(OpCodes.Stfld, fi);
799 PropertyInfo pi = binding.Member as PropertyInfo;
801 EmitCall(objectType, pi.GetSetMethod(true));
803 throw Error.UnhandledBinding();
808 private void EmitMemberMemberBinding(MemberMemberBinding binding) {
809 Type type = GetMemberType(binding.Member);
810 if (binding.Member is PropertyInfo && type.IsValueType) {
811 throw Error.CannotAutoInitializeValueTypeMemberThroughProperty(binding.Member);
813 if (type.IsValueType) {
814 EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
816 EmitMemberGet(binding.Member, binding.Member.DeclaringType);
818 EmitMemberInit(binding.Bindings, false, type);
821 private void EmitMemberListBinding(MemberListBinding binding) {
822 Type type = GetMemberType(binding.Member);
823 if (binding.Member is PropertyInfo && type.IsValueType) {
824 throw Error.CannotAutoInitializeValueTypeElementThroughProperty(binding.Member);
826 if (type.IsValueType) {
827 EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
829 EmitMemberGet(binding.Member, binding.Member.DeclaringType);
831 EmitListInit(binding.Initializers, false, type);
834 private void EmitMemberInit(MemberInitExpression init) {
835 EmitExpression(init.NewExpression);
836 LocalBuilder loc = null;
837 if (init.NewExpression.Type.IsValueType && init.Bindings.Count > 0) {
838 loc = _ilg.DeclareLocal(init.NewExpression.Type);
839 _ilg.Emit(OpCodes.Stloc, loc);
840 _ilg.Emit(OpCodes.Ldloca, loc);
842 EmitMemberInit(init.Bindings, loc == null, init.NewExpression.Type);
844 _ilg.Emit(OpCodes.Ldloc, loc);
848 // This method assumes that the instance is on the stack and is expected, based on "keepOnStack" flag
849 // to either leave the instance on the stack, or pop it.
850 private void EmitMemberInit(ReadOnlyCollection<MemberBinding> bindings, bool keepOnStack, Type objectType) {
851 int n = bindings.Count;
853 // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
855 _ilg.Emit(OpCodes.Pop);
858 for (int i = 0; i < n; i++) {
859 if (keepOnStack || i < n - 1) {
860 _ilg.Emit(OpCodes.Dup);
862 EmitBinding(bindings[i], objectType);
867 private void EmitListInit(ListInitExpression init) {
868 EmitExpression(init.NewExpression);
869 LocalBuilder loc = null;
870 if (init.NewExpression.Type.IsValueType) {
871 loc = _ilg.DeclareLocal(init.NewExpression.Type);
872 _ilg.Emit(OpCodes.Stloc, loc);
873 _ilg.Emit(OpCodes.Ldloca, loc);
875 EmitListInit(init.Initializers, loc == null, init.NewExpression.Type);
877 _ilg.Emit(OpCodes.Ldloc, loc);
881 // This method assumes that the list instance is on the stack and is expected, based on "keepOnStack" flag
882 // to either leave the list instance on the stack, or pop it.
883 private void EmitListInit(ReadOnlyCollection<ElementInit> initializers, bool keepOnStack, Type objectType) {
884 int n = initializers.Count;
887 // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
889 _ilg.Emit(OpCodes.Pop);
892 for (int i = 0; i < n; i++) {
893 if (keepOnStack || i < n - 1) {
894 _ilg.Emit(OpCodes.Dup);
896 EmitMethodCall(initializers[i].AddMethod, initializers[i], objectType);
898 // Aome add methods, ArrayList.Add for example, return non-void
899 if (initializers[i].AddMethod.ReturnType != typeof(void)) {
900 _ilg.Emit(OpCodes.Pop);
906 private static Type GetMemberType(MemberInfo member) {
907 FieldInfo fi = member as FieldInfo;
908 if (fi != null) return fi.FieldType;
909 PropertyInfo pi = member as PropertyInfo;
910 if (pi != null) return pi.PropertyType;
911 throw Error.MemberNotFieldOrProperty(member);
916 #region Expression helpers
918 internal static void ValidateLift(IList<ParameterExpression> variables, IList<Expression> arguments) {
919 System.Diagnostics.Debug.Assert(variables != null);
920 System.Diagnostics.Debug.Assert(arguments != null);
922 if (variables.Count != arguments.Count) {
923 throw Error.IncorrectNumberOfIndexes();
925 for (int i = 0, n = variables.Count; i < n; i++) {
926 if (!TypeUtils.AreReferenceAssignable(variables[i].Type, TypeUtils.GetNonNullableType(arguments[i].Type))) {
927 throw Error.ArgumentTypesMustMatch();
932 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
933 private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpression mc, ParameterExpression[] paramList, Expression[] argList) {
934 Debug.Assert(TypeUtils.AreEquivalent(TypeUtils.GetNonNullableType(resultType), TypeUtils.GetNonNullableType(mc.Type)));
938 case ExpressionType.LessThan:
939 case ExpressionType.LessThanOrEqual:
940 case ExpressionType.GreaterThan:
941 case ExpressionType.GreaterThanOrEqual: {
942 Label exit = _ilg.DefineLabel();
943 Label exitNull = _ilg.DefineLabel();
944 LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool));
945 for (int i = 0, n = paramList.Length; i < n; i++) {
946 ParameterExpression v = paramList[i];
947 Expression arg = argList[i];
948 if (TypeUtils.IsNullableType(arg.Type)) {
949 _scope.AddLocal(this, v);
950 EmitAddress(arg, arg.Type);
951 _ilg.Emit(OpCodes.Dup);
952 _ilg.EmitHasValue(arg.Type);
953 _ilg.Emit(OpCodes.Ldc_I4_0);
954 _ilg.Emit(OpCodes.Ceq);
955 _ilg.Emit(OpCodes.Stloc, anyNull);
956 _ilg.EmitGetValueOrDefault(arg.Type);
959 _scope.AddLocal(this, v);
961 if (!arg.Type.IsValueType) {
962 _ilg.Emit(OpCodes.Dup);
963 _ilg.Emit(OpCodes.Ldnull);
964 _ilg.Emit(OpCodes.Ceq);
965 _ilg.Emit(OpCodes.Stloc, anyNull);
969 _ilg.Emit(OpCodes.Ldloc, anyNull);
970 _ilg.Emit(OpCodes.Brtrue, exitNull);
972 EmitMethodCallExpression(mc);
973 if (TypeUtils.IsNullableType(resultType) && !TypeUtils.AreEquivalent(resultType, mc.Type)) {
974 ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type });
975 _ilg.Emit(OpCodes.Newobj, ci);
977 _ilg.Emit(OpCodes.Br_S, exit);
978 _ilg.MarkLabel(exitNull);
979 if (TypeUtils.AreEquivalent(resultType, TypeUtils.GetNullableType(mc.Type))) {
980 if (resultType.IsValueType) {
981 LocalBuilder result = GetLocal(resultType);
982 _ilg.Emit(OpCodes.Ldloca, result);
983 _ilg.Emit(OpCodes.Initobj, resultType);
984 _ilg.Emit(OpCodes.Ldloc, result);
987 _ilg.Emit(OpCodes.Ldnull);
991 case ExpressionType.LessThan:
992 case ExpressionType.LessThanOrEqual:
993 case ExpressionType.GreaterThan:
994 case ExpressionType.GreaterThanOrEqual:
995 _ilg.Emit(OpCodes.Ldc_I4_0);
998 throw Error.UnknownLiftType(nodeType);
1001 _ilg.MarkLabel(exit);
1004 case ExpressionType.Equal:
1005 case ExpressionType.NotEqual: {
1006 if (TypeUtils.AreEquivalent(resultType, TypeUtils.GetNullableType(mc.Type))) {
1009 Label exit = _ilg.DefineLabel();
1010 Label exitAllNull = _ilg.DefineLabel();
1011 Label exitAnyNull = _ilg.DefineLabel();
1013 LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool));
1014 LocalBuilder allNull = _ilg.DeclareLocal(typeof(bool));
1015 _ilg.Emit(OpCodes.Ldc_I4_0);
1016 _ilg.Emit(OpCodes.Stloc, anyNull);
1017 _ilg.Emit(OpCodes.Ldc_I4_1);
1018 _ilg.Emit(OpCodes.Stloc, allNull);
1020 for (int i = 0, n = paramList.Length; i < n; i++) {
1021 ParameterExpression v = paramList[i];
1022 Expression arg = argList[i];
1023 _scope.AddLocal(this, v);
1024 if (TypeUtils.IsNullableType(arg.Type)) {
1025 EmitAddress(arg, arg.Type);
1026 _ilg.Emit(OpCodes.Dup);
1027 _ilg.EmitHasValue(arg.Type);
1028 _ilg.Emit(OpCodes.Ldc_I4_0);
1029 _ilg.Emit(OpCodes.Ceq);
1030 _ilg.Emit(OpCodes.Dup);
1031 _ilg.Emit(OpCodes.Ldloc, anyNull);
1032 _ilg.Emit(OpCodes.Or);
1033 _ilg.Emit(OpCodes.Stloc, anyNull);
1034 _ilg.Emit(OpCodes.Ldloc, allNull);
1035 _ilg.Emit(OpCodes.And);
1036 _ilg.Emit(OpCodes.Stloc, allNull);
1037 _ilg.EmitGetValueOrDefault(arg.Type);
1039 EmitExpression(arg);
1040 if (!arg.Type.IsValueType) {
1041 _ilg.Emit(OpCodes.Dup);
1042 _ilg.Emit(OpCodes.Ldnull);
1043 _ilg.Emit(OpCodes.Ceq);
1044 _ilg.Emit(OpCodes.Dup);
1045 _ilg.Emit(OpCodes.Ldloc, anyNull);
1046 _ilg.Emit(OpCodes.Or);
1047 _ilg.Emit(OpCodes.Stloc, anyNull);
1048 _ilg.Emit(OpCodes.Ldloc, allNull);
1049 _ilg.Emit(OpCodes.And);
1050 _ilg.Emit(OpCodes.Stloc, allNull);
1052 _ilg.Emit(OpCodes.Ldc_I4_0);
1053 _ilg.Emit(OpCodes.Stloc, allNull);
1058 _ilg.Emit(OpCodes.Ldloc, allNull);
1059 _ilg.Emit(OpCodes.Brtrue, exitAllNull);
1060 _ilg.Emit(OpCodes.Ldloc, anyNull);
1061 _ilg.Emit(OpCodes.Brtrue, exitAnyNull);
1063 EmitMethodCallExpression(mc);
1064 if (TypeUtils.IsNullableType(resultType) && !TypeUtils.AreEquivalent(resultType, mc.Type)) {
1065 ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type });
1066 _ilg.Emit(OpCodes.Newobj, ci);
1068 _ilg.Emit(OpCodes.Br_S, exit);
1070 _ilg.MarkLabel(exitAllNull);
1071 _ilg.EmitBoolean(nodeType == ExpressionType.Equal);
1072 _ilg.Emit(OpCodes.Br_S, exit);
1074 _ilg.MarkLabel(exitAnyNull);
1075 _ilg.EmitBoolean(nodeType == ExpressionType.NotEqual);
1077 _ilg.MarkLabel(exit);