svn path=/trunk/mcs/; revision=104772
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / EmitContext.cs
1 //
2 // EmitContext.cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@novell.com)
6 //   Jb Evain (jbevain@novell.com)
7 //
8 // (C) 2008 Novell, Inc. (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Collections.ObjectModel;
32 using System.Collections.Generic;
33 using System.IO;
34 using System.Linq;
35 using System.Reflection;
36 using System.Reflection.Emit;
37 using System.Runtime.CompilerServices;
38
39 namespace System.Linq.Expressions {
40
41         abstract class EmitContext {
42
43                 protected LambdaExpression owner;
44                 protected Type [] param_types;
45                 protected Type return_type;
46
47                 protected List<object> globals = new List<object> ();
48
49                 public ILGenerator ig;
50
51                 protected EmitContext (LambdaExpression lambda)
52                 {
53                         this.owner = lambda;
54
55                         param_types = CreateParameterTypes (owner.Parameters);
56                         return_type = owner.GetReturnType ();
57                 }
58
59                 static Type [] CreateParameterTypes (ReadOnlyCollection<ParameterExpression> parameters)
60                 {
61                         var types = new Type [parameters.Count + 1];
62                         types [0] = typeof (ExecutionScope);
63
64                         for (int i = 0; i < parameters.Count; i++)
65                                 types [i + 1] = parameters [i].Type;
66
67                         return types;
68                 }
69
70                 public static EmitContext Create (LambdaExpression lambda)
71                 {
72 #if !NET_2_1
73                         if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null)
74                                 return new DebugEmitContext (lambda);
75 #endif
76                         return new DynamicEmitContext (lambda);
77                 }
78
79                 public int GetParameterPosition (ParameterExpression p)
80                 {
81                         int position = owner.Parameters.IndexOf (p);
82                         if (position == -1)
83                                 throw new InvalidOperationException ("Parameter not in scope");
84
85                         return position + 1; // + 1 because 0 is the ExecutionScope
86                 }
87
88                 public abstract Delegate CreateDelegate ();
89
90                 public void Emit (Expression expression)
91                 {
92                         expression.Emit (this);
93                 }
94
95                 public LocalBuilder EmitStored (Expression expression)
96                 {
97                         var local = ig.DeclareLocal (expression.Type);
98                         expression.Emit (this);
99                         ig.Emit (OpCodes.Stloc, local);
100
101                         return local;
102                 }
103
104                 public void EmitLoadAddress (Expression expression)
105                 {
106                         ig.Emit (OpCodes.Ldloca, EmitStored (expression));
107                 }
108
109                 public void EmitLoadSubject (Expression expression)
110                 {
111                         if (expression.Type.IsValueType) {
112                                 EmitLoadAddress (expression);
113                                 return;
114                         }
115
116                         Emit (expression);
117                 }
118
119                 public void EmitLoadSubject (LocalBuilder local)
120                 {
121                         if (local.LocalType.IsValueType) {
122                                 EmitLoadAddress (local);
123                                 return;
124                         }
125
126                         EmitLoad (local);
127                 }
128
129                 public void EmitLoadAddress (LocalBuilder local)
130                 {
131                         ig.Emit (OpCodes.Ldloca, local);
132                 }
133
134                 public void EmitLoad (LocalBuilder local)
135                 {
136                         ig.Emit (OpCodes.Ldloc, local);
137                 }
138
139                 public void EmitCall (LocalBuilder local, ReadOnlyCollection<Expression> arguments, MethodInfo method)
140                 {
141                         EmitLoadSubject (local);
142                         EmitArguments (method, arguments);
143                         EmitCall (method);
144                 }
145
146                 public void EmitCall (LocalBuilder local, MethodInfo method)
147                 {
148                         EmitLoadSubject (local);
149                         EmitCall (method);
150                 }
151
152                 public void EmitCall (Expression expression, MethodInfo method)
153                 {
154                         if (!method.IsStatic)
155                                 EmitLoadSubject (expression);
156
157                         EmitCall (method);
158                 }
159
160                 public void EmitCall (Expression expression, ReadOnlyCollection<Expression> arguments, MethodInfo method)
161                 {
162                         if (!method.IsStatic)
163                                 EmitLoadSubject (expression);
164
165                         EmitArguments (method, arguments);
166                         EmitCall (method);
167                 }
168
169                 void EmitArguments (MethodInfo method, ReadOnlyCollection<Expression> arguments)
170                 {
171                         var parameters = method.GetParameters ();
172
173                         for (int i = 0; i < parameters.Length; i++) {
174                                 var parameter = parameters [i];
175                                 var argument = arguments [i];
176
177                                 if (parameter.ParameterType.IsByRef) {
178                                         ig.Emit (OpCodes.Ldloca, EmitStored (argument));
179                                         return;
180                                 }
181
182                                 Emit (arguments [i]);
183                         }
184                 }
185
186                 public void EmitCall (MethodInfo method)
187                 {
188                         ig.Emit (
189                                 method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
190                                 method);
191                 }
192
193                 public void EmitNullableHasValue (LocalBuilder local)
194                 {
195                         EmitCall (local, local.LocalType.GetMethod ("get_HasValue"));
196                 }
197
198                 public void EmitNullableInitialize (LocalBuilder local)
199                 {
200                         ig.Emit (OpCodes.Ldloca, local);
201                         ig.Emit (OpCodes.Initobj, local.LocalType);
202                         ig.Emit (OpCodes.Ldloc, local);
203                 }
204
205                 public void EmitNullableGetValue (LocalBuilder local)
206                 {
207                         EmitCall (local, local.LocalType.GetMethod ("get_Value", Type.EmptyTypes));
208                 }
209
210                 public void EmitNullableGetValueOrDefault (LocalBuilder local)
211                 {
212                         EmitCall (local, local.LocalType.GetMethod ("GetValueOrDefault", Type.EmptyTypes));
213                 }
214
215                 public void EmitNullableNew (Type of)
216                 {
217                         ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
218                 }
219
220                 public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
221                 {
222                         foreach (var expression in collection)
223                                 expression.Emit (this);
224                 }
225
226                 public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
227                 {
228                         foreach (var initializer in initializers)
229                                 initializer.Emit (this, local);
230                 }
231
232                 public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
233                 {
234                         foreach (var binding in bindings)
235                                 binding.Emit (this, local);
236                 }
237
238                 public void EmitIsInst (Expression expression, Type candidate)
239                 {
240                         expression.Emit (this);
241
242                         if (expression.Type.IsValueType)
243                                 ig.Emit (OpCodes.Box, expression.Type);
244
245                         ig.Emit (OpCodes.Isinst, candidate);
246                 }
247
248                 public void EmitConvert (LocalBuilder local, Type to)
249                 {
250
251                 }
252
253                 public void EmitScope ()
254                 {
255                         ig.Emit (OpCodes.Ldarg_0);
256                 }
257
258                 public void EmitReadGlobal (object global)
259                 {
260                         EmitReadGlobal (global, global.GetType ());
261                 }
262
263                 public void EmitReadGlobal (object global, Type type)
264                 {
265                         EmitScope ();
266
267                         ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
268
269                         ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
270                         ig.Emit (OpCodes.Ldelem, typeof (object));
271
272                         var strongbox = type.MakeStrongBoxType ();
273
274                         ig.Emit (OpCodes.Isinst, strongbox);
275                         ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
276                 }
277
278                 int AddGlobal (object value, Type type)
279                 {
280                         globals.Add (CreateStrongBox (value, type));
281                         return globals.Count - 1;
282                 }
283
284                 static object CreateStrongBox (object value, Type type)
285                 {
286                         return Activator.CreateInstance (
287                                 type.MakeStrongBoxType (), value);
288                 }
289         }
290
291         class DynamicEmitContext : EmitContext {
292
293                 DynamicMethod method;
294
295                 public DynamicMethod Method {
296                         get { return method; }
297                 }
298
299                 public DynamicEmitContext (LambdaExpression lambda)
300                         : base (lambda)
301                 {
302                         // FIXME: Need to force this to be verifiable, see:
303                         // https://bugzilla.novell.com/show_bug.cgi?id=355005
304                         method = new DynamicMethod (GenerateName (), return_type, param_types, typeof (ExecutionScope), true);
305                         ig = method.GetILGenerator ();
306
307                         owner.EmitBody (this);
308                 }
309
310                 public override Delegate CreateDelegate ()
311                 {
312                         return method.CreateDelegate (owner.Type, new ExecutionScope (globals.ToArray ()));
313                 }
314
315                 protected virtual string GenerateName ()
316                 {
317                         return "lambda_method";
318                 }
319         }
320
321 #if !NET_2_1
322         class DebugEmitContext : DynamicEmitContext {
323
324                 static object mlock = new object ();
325                 static int method_count;
326
327                 public DebugEmitContext (LambdaExpression lambda)
328                         : base (lambda)
329                 {
330                         var name = Method.Name;
331                         var file_name = name + ".dll";
332
333                         var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (
334                                 new AssemblyName (name), AssemblyBuilderAccess.RunAndSave, Path.GetTempPath ());
335
336                         var type = assembly.DefineDynamicModule (file_name, file_name).DefineType ("Linq", TypeAttributes.Public);
337
338                         var method = type.DefineMethod (name, MethodAttributes.Public | MethodAttributes.Static, return_type, param_types);
339                         ig = method.GetILGenerator ();
340
341                         owner.EmitBody (this);
342
343                         type.CreateType ();
344                         assembly.Save (file_name);
345                 }
346
347                 protected override string GenerateName ()
348                 {
349                         lock (mlock) {
350                                 return "lambda_method-" + (method_count++);
351                         }
352                 }
353         }
354 #endif
355 }