1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation.
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 
16 using System;
17 using System.Collections.Generic;
18 using System.Diagnostics;
19 using System.Dynamic.Utils;
20 using System.Reflection;
21 using System.Reflection.Emit;
22 
23 #if CLR2
24 namespace Microsoft.Scripting.Ast.Compiler {
25 #else
26 namespace System.Linq.Expressions.Compiler {
27 #endif
28     partial class LambdaCompiler {
EmitAddress(Expression node, Type type)29         private void EmitAddress(Expression node, Type type) {
30             EmitAddress(node, type, CompilationFlags.EmitExpressionStart);
31         }
32 
33         // We don't want "ref" parameters to modify values of expressions
34         // except where it would in IL: locals, args, fields, and array elements
35         // (Unbox is an exception, it's intended to emit a ref to the orignal
36         // boxed value)
EmitAddress(Expression node, Type type, CompilationFlags flags)37         private void EmitAddress(Expression node, Type type, CompilationFlags flags) {
38             Debug.Assert(node != null);
39             bool emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart;
40             CompilationFlags startEmitted = emitStart ? EmitExpressionStart(node) : CompilationFlags.EmitNoExpressionStart;
41 
42             switch (node.NodeType) {
43                 default:
44                     EmitExpressionAddress(node, type);
45                     break;
46 
47                 case ExpressionType.ArrayIndex:
48                     AddressOf((BinaryExpression)node, type);
49                     break;
50 
51                 case ExpressionType.Parameter:
52                     AddressOf((ParameterExpression)node, type);
53                     break;
54 
55                 case ExpressionType.MemberAccess:
56                     AddressOf((MemberExpression)node, type);
57                     break;
58 
59                 case ExpressionType.Unbox:
60                     AddressOf((UnaryExpression)node, type);
61                     break;
62 
63                 case ExpressionType.Call:
64                     AddressOf((MethodCallExpression)node, type);
65                     break;
66 
67                 case ExpressionType.Index:
68                     AddressOf((IndexExpression)node, type);
69                     break;
70             }
71 
72             if (emitStart) {
73                 EmitExpressionEnd(startEmitted);
74             }
75         }
76 
77 
AddressOf(BinaryExpression node, Type type)78         private void AddressOf(BinaryExpression node, Type type) {
79             Debug.Assert(node.NodeType == ExpressionType.ArrayIndex && node.Method == null);
80 
81             if (TypeUtils.AreEquivalent(type, node.Type)) {
82                 EmitExpression(node.Left);
83                 EmitExpression(node.Right);
84                 Type rightType = node.Right.Type;
85                 if (TypeUtils.IsNullableType(rightType)) {
86                     LocalBuilder loc = GetLocal(rightType);
87                     _ilg.Emit(OpCodes.Stloc, loc);
88                     _ilg.Emit(OpCodes.Ldloca, loc);
89                     _ilg.EmitGetValue(rightType);
90                     FreeLocal(loc);
91                 }
92                 Type indexType = TypeUtils.GetNonNullableType(rightType);
93                 if (indexType != typeof(int)) {
94                     _ilg.EmitConvertToType(indexType, typeof(int), true);
95                 }
96                 _ilg.Emit(OpCodes.Ldelema, node.Type);
97             } else {
98                 EmitExpressionAddress(node, type);
99             }
100         }
101 
AddressOf(ParameterExpression node, Type type)102         private void AddressOf(ParameterExpression node, Type type) {
103             if (TypeUtils.AreEquivalent(type, node.Type)) {
104                 if (node.IsByRef) {
105                     _scope.EmitGet(node);
106                 } else {
107                     _scope.EmitAddressOf(node);
108                 }
109             } else {
110                 EmitExpressionAddress(node, type);
111             }
112         }
113 
114 
AddressOf(MemberExpression node, Type type)115         private void AddressOf(MemberExpression node, Type type) {
116             if (TypeUtils.AreEquivalent(type, node.Type)) {
117                 // emit "this", if any
118                 Type objectType = null;
119                 if (node.Expression != null) {
120                     EmitInstance(node.Expression, objectType = node.Expression.Type);
121                 }
122                 EmitMemberAddress(node.Member, objectType);
123             } else {
124                 EmitExpressionAddress(node, type);
125             }
126         }
127 
128         // assumes the instance is already on the stack
EmitMemberAddress(MemberInfo member, Type objectType)129         private void EmitMemberAddress(MemberInfo member, Type objectType) {
130             if (member.MemberType == MemberTypes.Field) {
131                 FieldInfo field = (FieldInfo)member;
132 
133                 // Verifiable code may not take the address of an init-only field.
134                 // If we are asked to do so then get the value out of the field, stuff it
135                 // into a local of the same type, and then take the address of the local.
136                 // Typically this is what we want to do anyway; if we are saying
137                 // Foo.bar.ToString() for a static value-typed field bar then we don't need
138                 // the address of field bar to do the call.  The address of a local which
139                 // has the same value as bar is sufficient.
140 
141                 //
142 
143 
144 
145 
146 
147 
148 
149 
150                 if (!field.IsLiteral && !field.IsInitOnly) {
151                     _ilg.EmitFieldAddress(field);
152                     return;
153                 }
154             }
155 
156             EmitMemberGet(member, objectType);
157             LocalBuilder temp = GetLocal(GetMemberType(member));
158             _ilg.Emit(OpCodes.Stloc, temp);
159             _ilg.Emit(OpCodes.Ldloca, temp);
160         }
161 
162 
AddressOf(MethodCallExpression node, Type type)163         private void AddressOf(MethodCallExpression node, Type type) {
164             // An array index of a multi-dimensional array is represented by a call to Array.Get,
165             // rather than having its own array-access node. This means that when we are trying to
166             // get the address of a member of a multi-dimensional array, we'll be trying to
167             // get the address of a Get method, and it will fail to do so. Instead, detect
168             // this situation and replace it with a call to the Address method.
169             if (!node.Method.IsStatic &&
170                 node.Object.Type.IsArray &&
171                 node.Method == node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)) {
172 
173                 MethodInfo mi = node.Object.Type.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance);
174 
175                 EmitMethodCall(node.Object, mi, node);
176             } else {
177                 EmitExpressionAddress(node, type);
178             }
179         }
180 
AddressOf(IndexExpression node, Type type)181         private void AddressOf(IndexExpression node, Type type) {
182             if (!TypeUtils.AreEquivalent(type, node.Type) || node.Indexer != null) {
183                 EmitExpressionAddress(node, type);
184                 return;
185             }
186 
187             if (node.Arguments.Count == 1) {
188                 EmitExpression(node.Object);
189                 EmitExpression(node.Arguments[0]);
190                 _ilg.Emit(OpCodes.Ldelema, node.Type);
191             } else {
192                 var address = node.Object.Type.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance);
193                 EmitMethodCall(node.Object, address, node);
194             }
195         }
196 
AddressOf(UnaryExpression node, Type type)197         private void AddressOf(UnaryExpression node, Type type) {
198             Debug.Assert(node.NodeType == ExpressionType.Unbox);
199             Debug.Assert(type.IsValueType && !TypeUtils.IsNullableType(type));
200 
201             // Unbox leaves a pointer to the boxed value on the stack
202             EmitExpression(node.Operand);
203             _ilg.Emit(OpCodes.Unbox, type);
204         }
205 
EmitExpressionAddress(Expression node, Type type)206         private void EmitExpressionAddress(Expression node, Type type) {
207             Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type));
208 
209             EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
210             LocalBuilder tmp = GetLocal(type);
211             _ilg.Emit(OpCodes.Stloc, tmp);
212             _ilg.Emit(OpCodes.Ldloca, tmp);
213         }
214 
215 
216         // Emits the address of the expression, returning the write back if necessary
217         //
218         // For properties, we want to write back into the property if it's
219         // passed byref.
EmitAddressWriteBack(Expression node, Type type)220         private WriteBack EmitAddressWriteBack(Expression node, Type type) {
221             CompilationFlags startEmitted = EmitExpressionStart(node);
222 
223             WriteBack result = null;
224             if (TypeUtils.AreEquivalent(type, node.Type)) {
225                 switch (node.NodeType) {
226                     case ExpressionType.MemberAccess:
227                         result = AddressOfWriteBack((MemberExpression)node);
228                         break;
229                     case ExpressionType.Index:
230                         result = AddressOfWriteBack((IndexExpression)node);
231                         break;
232                 }
233             }
234             if (result == null) {
235                 EmitAddress(node, type, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
236             }
237 
238             EmitExpressionEnd(startEmitted);
239 
240             return result;
241         }
242 
AddressOfWriteBack(MemberExpression node)243         private WriteBack AddressOfWriteBack(MemberExpression node) {
244             if (node.Member.MemberType != MemberTypes.Property || !((PropertyInfo)node.Member).CanWrite) {
245                 return null;
246             }
247 
248             // emit instance, if any
249             LocalBuilder instanceLocal = null;
250             Type instanceType = null;
251             if (node.Expression != null) {
252                 EmitInstance(node.Expression, instanceType = node.Expression.Type);
253                 // store in local
254                 _ilg.Emit(OpCodes.Dup);
255                 _ilg.Emit(OpCodes.Stloc, instanceLocal = GetLocal(instanceType));
256             }
257 
258             PropertyInfo pi = (PropertyInfo)node.Member;
259 
260             // emit the get
261             EmitCall(instanceType, pi.GetGetMethod(true));
262 
263             // emit the address of the value
264             var valueLocal = GetLocal(node.Type);
265             _ilg.Emit(OpCodes.Stloc, valueLocal);
266             _ilg.Emit(OpCodes.Ldloca, valueLocal);
267 
268             // Set the property after the method call
269             // don't re-evaluate anything
270             return delegate() {
271                 if (instanceLocal != null) {
272                     _ilg.Emit(OpCodes.Ldloc, instanceLocal);
273                     FreeLocal(instanceLocal);
274                 }
275                 _ilg.Emit(OpCodes.Ldloc, valueLocal);
276                 FreeLocal(valueLocal);
277                 EmitCall(instanceType, pi.GetSetMethod(true));
278             };
279         }
280 
AddressOfWriteBack(IndexExpression node)281         private WriteBack AddressOfWriteBack(IndexExpression node) {
282             if (node.Indexer == null || !node.Indexer.CanWrite) {
283                 return null;
284             }
285 
286             // emit instance, if any
287             LocalBuilder instanceLocal = null;
288             Type instanceType = null;
289             if (node.Object != null) {
290                 EmitInstance(node.Object, instanceType = node.Object.Type);
291 
292                 _ilg.Emit(OpCodes.Dup);
293                 _ilg.Emit(OpCodes.Stloc, instanceLocal = GetLocal(instanceType));
294             }
295 
296             // Emit indexes. We don't allow byref args, so no need to worry
297             // about writebacks or EmitAddress
298             List<LocalBuilder> args = new List<LocalBuilder>();
299             foreach (var arg in node.Arguments) {
300                 EmitExpression(arg);
301 
302                 var argLocal = GetLocal(arg.Type);
303                 _ilg.Emit(OpCodes.Dup);
304                 _ilg.Emit(OpCodes.Stloc, argLocal);
305                 args.Add(argLocal);
306             }
307 
308             // emit the get
309             EmitGetIndexCall(node, instanceType);
310 
311             // emit the address of the value
312             var valueLocal = GetLocal(node.Type);
313             _ilg.Emit(OpCodes.Stloc, valueLocal);
314             _ilg.Emit(OpCodes.Ldloca, valueLocal);
315 
316             // Set the property after the method call
317             // don't re-evaluate anything
318             return delegate() {
319                 if (instanceLocal != null) {
320                     _ilg.Emit(OpCodes.Ldloc, instanceLocal);
321                     FreeLocal(instanceLocal);
322                 }
323                 foreach (var arg in args) {
324                     _ilg.Emit(OpCodes.Ldloc, arg);
325                     FreeLocal(arg);
326                 }
327                 _ilg.Emit(OpCodes.Ldloc, valueLocal);
328                 FreeLocal(valueLocal);
329 
330                 EmitSetIndexCall(node, instanceType);
331             };
332         }
333     }
334 }
335