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
.Diagnostics
;
19 using System
.Dynamic
.Utils
;
20 using System
.Reflection
;
21 using System
.Reflection
.Emit
;
24 namespace Microsoft
.Scripting
.Ast
.Compiler
{
26 namespace System
.Linq
.Expressions
.Compiler
{
28 partial class LambdaCompiler
{
29 private void EmitAddress(Expression node
, Type type
) {
30 EmitAddress(node
, type
, CompilationFlags
.EmitExpressionStart
);
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
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
;
42 switch (node
.NodeType
) {
44 EmitExpressionAddress(node
, type
);
47 case ExpressionType
.ArrayIndex
:
48 AddressOf((BinaryExpression
)node
, type
);
51 case ExpressionType
.Parameter
:
52 AddressOf((ParameterExpression
)node
, type
);
55 case ExpressionType
.MemberAccess
:
56 AddressOf((MemberExpression
)node
, type
);
59 case ExpressionType
.Unbox
:
60 AddressOf((UnaryExpression
)node
, type
);
63 case ExpressionType
.Call
:
64 AddressOf((MethodCallExpression
)node
, type
);
67 case ExpressionType
.Index
:
68 AddressOf((IndexExpression
)node
, type
);
73 EmitExpressionEnd(startEmitted
);
78 private void AddressOf(BinaryExpression node
, Type type
) {
79 Debug
.Assert(node
.NodeType
== ExpressionType
.ArrayIndex
&& node
.Method
== null);
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
);
92 Type indexType
= TypeUtils
.GetNonNullableType(rightType
);
93 if (indexType
!= typeof(int)) {
94 _ilg
.EmitConvertToType(indexType
, typeof(int), true);
96 _ilg
.Emit(OpCodes
.Ldelema
, node
.Type
);
98 EmitExpressionAddress(node
, type
);
102 private void AddressOf(ParameterExpression node
, Type type
) {
103 if (TypeUtils
.AreEquivalent(type
, node
.Type
)) {
105 _scope
.EmitGet(node
);
107 _scope
.EmitAddressOf(node
);
110 EmitExpressionAddress(node
, 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
);
122 EmitMemberAddress(node
.Member
, objectType
);
124 EmitExpressionAddress(node
, type
);
128 // assumes the instance is already on the stack
129 private void EmitMemberAddress(MemberInfo member
, Type objectType
) {
130 if (member
.MemberType
== MemberTypes
.Field
) {
131 FieldInfo field
= (FieldInfo
)member
;
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.
142 // The C# compiler will not compile a lambda expression tree
143 // which writes to the address of an init-only field. But one could
144 // probably use the expression tree API to build such an expression.
145 // (When compiled, such an expression would fail silently.) It might
146 // be worth it to add checking to the expression tree API to ensure
147 // that it is illegal to attempt to write to an init-only field,
148 // the same way that it is illegal to write to a read-only property.
149 // The same goes for literal fields.
150 if (!field
.IsLiteral
&& !field
.IsInitOnly
) {
151 _ilg
.EmitFieldAddress(field
);
156 EmitMemberGet(member
, objectType
);
157 LocalBuilder temp
= GetLocal(GetMemberType(member
));
158 _ilg
.Emit(OpCodes
.Stloc
, temp
);
159 _ilg
.Emit(OpCodes
.Ldloca
, temp
);
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
)) {
173 MethodInfo mi
= node
.Object
.Type
.GetMethod("Address", BindingFlags
.Public
| BindingFlags
.Instance
);
175 EmitMethodCall(node
.Object
, mi
, node
);
177 EmitExpressionAddress(node
, type
);
181 private void AddressOf(IndexExpression node
, Type type
) {
182 if (!TypeUtils
.AreEquivalent(type
, node
.Type
) || node
.Indexer
!= null) {
183 EmitExpressionAddress(node
, type
);
187 if (node
.Arguments
.Count
== 1) {
188 EmitExpression(node
.Object
);
189 EmitExpression(node
.Arguments
[0]);
190 _ilg
.Emit(OpCodes
.Ldelema
, node
.Type
);
192 var address
= node
.Object
.Type
.GetMethod("Address", BindingFlags
.Public
| BindingFlags
.Instance
);
193 EmitMethodCall(node
.Object
, address
, node
);
197 private void AddressOf(UnaryExpression node
, Type type
) {
198 Debug
.Assert(node
.NodeType
== ExpressionType
.Unbox
);
199 Debug
.Assert(type
.IsValueType
&& !TypeUtils
.IsNullableType(type
));
201 // Unbox leaves a pointer to the boxed value on the stack
202 EmitExpression(node
.Operand
);
203 _ilg
.Emit(OpCodes
.Unbox
, type
);
206 private void EmitExpressionAddress(Expression node
, Type type
) {
207 Debug
.Assert(TypeUtils
.AreReferenceAssignable(type
, node
.Type
));
209 EmitExpression(node
, CompilationFlags
.EmitAsNoTail
| CompilationFlags
.EmitNoExpressionStart
);
210 LocalBuilder tmp
= GetLocal(type
);
211 _ilg
.Emit(OpCodes
.Stloc
, tmp
);
212 _ilg
.Emit(OpCodes
.Ldloca
, tmp
);
216 // Emits the address of the expression, returning the write back if necessary
218 // For properties, we want to write back into the property if it's
220 private WriteBack
EmitAddressWriteBack(Expression node
, Type type
) {
221 CompilationFlags startEmitted
= EmitExpressionStart(node
);
223 WriteBack result
= null;
224 if (TypeUtils
.AreEquivalent(type
, node
.Type
)) {
225 switch (node
.NodeType
) {
226 case ExpressionType
.MemberAccess
:
227 result
= AddressOfWriteBack((MemberExpression
)node
);
229 case ExpressionType
.Index
:
230 result
= AddressOfWriteBack((IndexExpression
)node
);
234 if (result
== null) {
235 EmitAddress(node
, type
, CompilationFlags
.EmitAsNoTail
| CompilationFlags
.EmitNoExpressionStart
);
238 EmitExpressionEnd(startEmitted
);
243 private WriteBack
AddressOfWriteBack(MemberExpression node
) {
244 if (node
.Member
.MemberType
!= MemberTypes
.Property
|| !((PropertyInfo
)node
.Member
).CanWrite
) {
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
);
254 _ilg
.Emit(OpCodes
.Dup
);
255 _ilg
.Emit(OpCodes
.Stloc
, instanceLocal
= GetLocal(instanceType
));
258 PropertyInfo pi
= (PropertyInfo
)node
.Member
;
261 EmitCall(instanceType
, pi
.GetGetMethod(true));
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
);
268 // Set the property after the method call
269 // don't re-evaluate anything
271 if (instanceLocal
!= null) {
272 _ilg
.Emit(OpCodes
.Ldloc
, instanceLocal
);
273 FreeLocal(instanceLocal
);
275 _ilg
.Emit(OpCodes
.Ldloc
, valueLocal
);
276 FreeLocal(valueLocal
);
277 EmitCall(instanceType
, pi
.GetSetMethod(true));
281 private WriteBack
AddressOfWriteBack(IndexExpression node
) {
282 if (node
.Indexer
== null || !node
.Indexer
.CanWrite
) {
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
);
292 _ilg
.Emit(OpCodes
.Dup
);
293 _ilg
.Emit(OpCodes
.Stloc
, instanceLocal
= GetLocal(instanceType
));
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
) {
302 var argLocal
= GetLocal(arg
.Type
);
303 _ilg
.Emit(OpCodes
.Dup
);
304 _ilg
.Emit(OpCodes
.Stloc
, argLocal
);
309 EmitGetIndexCall(node
, instanceType
);
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
);
316 // Set the property after the method call
317 // don't re-evaluate anything
319 if (instanceLocal
!= null) {
320 _ilg
.Emit(OpCodes
.Ldloc
, instanceLocal
);
321 FreeLocal(instanceLocal
);
323 foreach (var arg
in args
) {
324 _ilg
.Emit(OpCodes
.Ldloc
, arg
);
327 _ilg
.Emit(OpCodes
.Ldloc
, valueLocal
);
328 FreeLocal(valueLocal
);
330 EmitSetIndexCall(node
, instanceType
);