Merge pull request #4155 from BrzVlad/fix-tls-lmf-addr
[mono-project.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.Address.cs
blob5b34dd25aed8eb1037df974162b4e73c07b337b6
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 * ***************************************************************************/
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;
23 #if !FEATURE_CORE_DLR
24 namespace Microsoft.Scripting.Ast.Compiler {
25 #else
26 namespace System.Linq.Expressions.Compiler {
27 #endif
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
36 // boxed value)
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) {
43 default:
44 EmitExpressionAddress(node, type);
45 break;
47 case ExpressionType.ArrayIndex:
48 AddressOf((BinaryExpression)node, type);
49 break;
51 case ExpressionType.Parameter:
52 AddressOf((ParameterExpression)node, type);
53 break;
55 case ExpressionType.MemberAccess:
56 AddressOf((MemberExpression)node, type);
57 break;
59 case ExpressionType.Unbox:
60 AddressOf((UnaryExpression)node, type);
61 break;
63 case ExpressionType.Call:
64 AddressOf((MethodCallExpression)node, type);
65 break;
67 case ExpressionType.Index:
68 AddressOf((IndexExpression)node, type);
69 break;
72 if (emitStart) {
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);
90 FreeLocal(loc);
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);
97 } else {
98 EmitExpressionAddress(node, 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);
109 } else {
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);
123 } else {
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.
141 // CONSIDER:
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);
152 return;
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);
176 } else {
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);
184 return;
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);
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
219 // passed byref.
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);
228 break;
229 case ExpressionType.Index:
230 result = AddressOfWriteBack((IndexExpression)node);
231 break;
234 if (result == null) {
235 EmitAddress(node, type, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
238 EmitExpressionEnd(startEmitted);
240 return result;
243 private WriteBack AddressOfWriteBack(MemberExpression node) {
244 if (node.Member.MemberType != MemberTypes.Property || !((PropertyInfo)node.Member).CanWrite) {
245 return null;
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));
258 PropertyInfo pi = (PropertyInfo)node.Member;
260 // emit the get
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
270 return delegate() {
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) {
283 return null;
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) {
300 EmitExpression(arg);
302 var argLocal = GetLocal(arg.Type);
303 _ilg.Emit(OpCodes.Dup);
304 _ilg.Emit(OpCodes.Stloc, argLocal);
305 args.Add(argLocal);
308 // emit the get
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
318 return delegate() {
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);
325 FreeLocal(arg);
327 _ilg.Emit(OpCodes.Ldloc, valueLocal);
328 FreeLocal(valueLocal);
330 EmitSetIndexCall(node, instanceType);