blah
[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         class CompilationContext {
42
43                 List<object> globals = new List<object> ();
44                 List<EmitContext> units = new List<EmitContext> ();
45
46                 public int AddGlobal (object global)
47                 {
48                         return AddItemToList (global, globals);
49                 }
50
51                 public object [] GetGlobals ()
52                 {
53                         return globals.ToArray ();
54                 }
55
56                 static int AddItemToList<T> (T item, IList<T> list)
57                 {
58                         list.Add (item);
59                         return list.Count - 1;
60                 }
61
62                 public int AddCompilationUnit (LambdaExpression lambda)
63                 {
64                         var context = new EmitContext (this, lambda);
65                         var unit = AddItemToList (context, units);
66                         context.Emit ();
67                         return unit;
68                 }
69
70                 public Delegate CreateDelegate ()
71                 {
72                         return CreateDelegate (0, new ExecutionScope (this));
73                 }
74
75                 public Delegate CreateDelegate (int unit, ExecutionScope scope)
76                 {
77                         return units [unit].CreateDelegate (scope);
78                 }
79         }
80
81         class EmitContext {
82
83                 LambdaExpression owner;
84                 CompilationContext context;
85                 DynamicMethod method;
86
87                 public ILGenerator ig;
88
89                 public EmitContext (CompilationContext context, LambdaExpression lambda)
90                 {
91                         this.context = context;
92                         this.owner = lambda;
93
94                         method = new DynamicMethod ("lambda_method", owner.GetReturnType (),
95                                 CreateParameterTypes (owner.Parameters), typeof (ExecutionScope), true);
96
97                         ig = method.GetILGenerator ();
98                 }
99
100                 public void Emit ()
101                 {
102                         owner.EmitBody (this);
103                 }
104
105                 static Type [] CreateParameterTypes (ReadOnlyCollection<ParameterExpression> parameters)
106                 {
107                         var types = new Type [parameters.Count + 1];
108                         types [0] = typeof (ExecutionScope);
109
110                         for (int i = 0; i < parameters.Count; i++)
111                                 types [i + 1] = parameters [i].Type;
112
113                         return types;
114                 }
115
116                 public int GetParameterPosition (ParameterExpression p)
117                 {
118                         int position = owner.Parameters.IndexOf (p);
119                         if (position == -1)
120                                 throw new InvalidOperationException ("Parameter not in scope");
121
122                         return position + 1; // + 1 because 0 is the ExecutionScope
123                 }
124
125                 public Delegate CreateDelegate (ExecutionScope scope)
126                 {
127                         return method.CreateDelegate (owner.Type, scope);
128                 }
129
130                 public void Emit (Expression expression)
131                 {
132                         expression.Emit (this);
133                 }
134
135                 public LocalBuilder EmitStored (Expression expression)
136                 {
137                         var local = ig.DeclareLocal (expression.Type);
138                         expression.Emit (this);
139                         ig.Emit (OpCodes.Stloc, local);
140
141                         return local;
142                 }
143
144                 public void EmitLoadAddress (Expression expression)
145                 {
146                         ig.Emit (OpCodes.Ldloca, EmitStored (expression));
147                 }
148
149                 public void EmitLoadSubject (Expression expression)
150                 {
151                         if (expression.Type.IsValueType) {
152                                 EmitLoadAddress (expression);
153                                 return;
154                         }
155
156                         Emit (expression);
157                 }
158
159                 public void EmitLoadSubject (LocalBuilder local)
160                 {
161                         if (local.LocalType.IsValueType) {
162                                 EmitLoadAddress (local);
163                                 return;
164                         }
165
166                         EmitLoad (local);
167                 }
168
169                 public void EmitLoadAddress (LocalBuilder local)
170                 {
171                         ig.Emit (OpCodes.Ldloca, local);
172                 }
173
174                 public void EmitLoad (LocalBuilder local)
175                 {
176                         ig.Emit (OpCodes.Ldloc, local);
177                 }
178
179                 public void EmitCall (LocalBuilder local, ReadOnlyCollection<Expression> arguments, MethodInfo method)
180                 {
181                         EmitLoadSubject (local);
182                         EmitArguments (method, arguments);
183                         EmitCall (method);
184                 }
185
186                 public void EmitCall (LocalBuilder local, MethodInfo method)
187                 {
188                         EmitLoadSubject (local);
189                         EmitCall (method);
190                 }
191
192                 public void EmitCall (Expression expression, MethodInfo method)
193                 {
194                         if (!method.IsStatic)
195                                 EmitLoadSubject (expression);
196
197                         EmitCall (method);
198                 }
199
200                 public void EmitCall (Expression expression, ReadOnlyCollection<Expression> arguments, MethodInfo method)
201                 {
202                         if (!method.IsStatic)
203                                 EmitLoadSubject (expression);
204
205                         EmitArguments (method, arguments);
206                         EmitCall (method);
207                 }
208
209                 void EmitArguments (MethodInfo method, ReadOnlyCollection<Expression> arguments)
210                 {
211                         var parameters = method.GetParameters ();
212
213                         for (int i = 0; i < parameters.Length; i++) {
214                                 var parameter = parameters [i];
215                                 var argument = arguments [i];
216
217                                 if (parameter.ParameterType.IsByRef) {
218                                         ig.Emit (OpCodes.Ldloca, EmitStored (argument));
219                                         continue;
220                                 }
221
222                                 Emit (arguments [i]);
223                         }
224                 }
225
226                 public void EmitCall (MethodInfo method)
227                 {
228                         ig.Emit (
229                                 method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
230                                 method);
231                 }
232
233                 public void EmitNullableHasValue (LocalBuilder local)
234                 {
235                         EmitCall (local, "get_HasValue");
236                 }
237
238                 public void EmitNullableInitialize (LocalBuilder local)
239                 {
240                         ig.Emit (OpCodes.Ldloca, local);
241                         ig.Emit (OpCodes.Initobj, local.LocalType);
242                         ig.Emit (OpCodes.Ldloc, local);
243                 }
244
245                 public void EmitNullableGetValue (LocalBuilder local)
246                 {
247                         EmitCall (local, "get_Value");
248                 }
249
250                 public void EmitNullableGetValueOrDefault (LocalBuilder local)
251                 {
252                         EmitCall (local, "GetValueOrDefault");
253                 }
254
255                 void EmitCall (LocalBuilder local, string method_name)
256                 {
257                         EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
258                 }
259
260                 public void EmitNullableNew (Type of)
261                 {
262                         ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
263                 }
264
265                 public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
266                 {
267                         foreach (var expression in collection)
268                                 expression.Emit (this);
269                 }
270
271                 public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
272                 {
273                         foreach (var initializer in initializers)
274                                 initializer.Emit (this, local);
275                 }
276
277                 public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
278                 {
279                         foreach (var binding in bindings)
280                                 binding.Emit (this, local);
281                 }
282
283                 public void EmitIsInst (Expression expression, Type candidate)
284                 {
285                         expression.Emit (this);
286
287                         var type = expression.Type;
288
289                         if (type.IsValueType)
290                                 ig.Emit (OpCodes.Box, type);
291
292                         ig.Emit (OpCodes.Isinst, candidate);
293                 }
294
295                 public void EmitScope ()
296                 {
297                         ig.Emit (OpCodes.Ldarg_0);
298                 }
299
300                 public void EmitReadGlobal (object global)
301                 {
302                         EmitReadGlobal (global, global.GetType ());
303                 }
304
305                 public void EmitReadGlobal (object global, Type type)
306                 {
307                         EmitScope ();
308
309                         ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
310
311                         ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
312                         ig.Emit (OpCodes.Ldelem, typeof (object));
313
314                         var strongbox = type.MakeStrongBoxType ();
315
316                         ig.Emit (OpCodes.Isinst, strongbox);
317                         ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
318                 }
319
320                 int AddGlobal (object value, Type type)
321                 {
322                         return context.AddGlobal (CreateStrongBox (value, type));
323                 }
324
325                 public void EmitCreateDelegate (LambdaExpression lambda)
326                 {
327                         EmitScope ();
328
329                         ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
330                         ig.Emit (OpCodes.Ldnull);
331
332                         ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
333
334                         ig.Emit (OpCodes.Castclass, lambda.Type);
335                 }
336
337                 int AddChildContext (LambdaExpression lambda)
338                 {
339                         return context.AddCompilationUnit (lambda);
340                 }
341
342                 static object CreateStrongBox (object value, Type type)
343                 {
344                         return Activator.CreateInstance (
345                                 type.MakeStrongBoxType (), value);
346                 }
347         }
348 }