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