oops
[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 EmitLoad (Expression expression)
105                 {
106                         if (expression.Type.IsValueType) {
107                                 var local = EmitStored (expression);
108                                 ig.Emit (OpCodes.Ldloca, local);
109                         } else
110                                 expression.Emit (this);
111                 }
112
113                 public void EmitLoad (LocalBuilder local)
114                 {
115                         ig.Emit (OpCodes.Ldloc, local);
116                 }
117
118                 public void EmitCall (LocalBuilder local, ReadOnlyCollection<Expression> arguments, MethodInfo method)
119                 {
120                         EmitLoad (local);
121                         EmitArguments (method, arguments);
122                         EmitCall (method);
123                 }
124
125                 public void EmitCall (Expression expression, ReadOnlyCollection<Expression> arguments, MethodInfo method)
126                 {
127                         if (!method.IsStatic)
128                                 EmitLoad (expression);
129
130                         EmitArguments (method, arguments);
131                         EmitCall (method);
132                 }
133
134                 void EmitArguments (MethodInfo method, ReadOnlyCollection<Expression> arguments)
135                 {
136                         var parameters = method.GetParameters ();
137
138                         for (int i = 0; i < parameters.Length; i++) {
139                                 var parameter = parameters [i];
140                                 var argument = arguments [i];
141
142                                 if (parameter.ParameterType.IsByRef) {
143                                         ig.Emit (OpCodes.Ldloca, EmitStored (argument));
144                                         return;
145                                 }
146
147                                 Emit (arguments [i]);
148                         }
149                 }
150
151                 public void EmitCall (MethodInfo method)
152                 {
153                         ig.Emit (
154                                 method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
155                                 method);
156                 }
157
158                 public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
159                 {
160                         foreach (var expression in collection)
161                                 expression.Emit (this);
162                 }
163
164                 public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
165                 {
166                         foreach (var initializer in initializers)
167                                 initializer.Emit (this, local);
168                 }
169
170                 public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
171                 {
172                         foreach (var binding in bindings)
173                                 binding.Emit (this, local);
174                 }
175
176                 public void EmitIsInst (Expression expression, Type candidate)
177                 {
178                         expression.Emit (this);
179
180                         if (expression.Type.IsValueType)
181                                 ig.Emit (OpCodes.Box, expression.Type);
182
183                         ig.Emit (OpCodes.Isinst, candidate);
184                 }
185
186                 public void EmitScope ()
187                 {
188                         ig.Emit (OpCodes.Ldarg_0);
189                 }
190
191                 public void EmitReadGlobal (object global)
192                 {
193                         EmitReadGlobal (global, global.GetType ());
194                 }
195
196                 public void EmitReadGlobal (object global, Type type)
197                 {
198                         EmitScope ();
199
200                         ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
201
202                         ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
203                         ig.Emit (OpCodes.Ldelem, typeof (object));
204
205                         var strongbox = type.MakeStrongBoxType ();
206
207                         ig.Emit (OpCodes.Isinst, strongbox);
208                         ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
209                 }
210
211                 int AddGlobal (object value, Type type)
212                 {
213                         globals.Add (CreateStrongBox (value, type));
214                         return globals.Count - 1;
215                 }
216
217                 static object CreateStrongBox (object value, Type type)
218                 {
219                         return Activator.CreateInstance (
220                                 type.MakeStrongBoxType (), value);
221                 }
222         }
223
224         class DynamicEmitContext : EmitContext {
225
226                 DynamicMethod method;
227
228                 static object mlock = new object ();
229                 static int method_count;
230
231                 public DynamicMethod Method {
232                         get { return method; }
233                 }
234
235                 public DynamicEmitContext (LambdaExpression lambda)
236                         : base (lambda)
237                 {
238                         // FIXME: Need to force this to be verifiable, see:
239                         // https://bugzilla.novell.com/show_bug.cgi?id=355005
240                         method = new DynamicMethod (GenerateName (), return_type, param_types, typeof (ExecutionScope), true);
241                         ig = method.GetILGenerator ();
242
243                         owner.Emit (this);
244                 }
245
246                 public override Delegate CreateDelegate ()
247                 {
248                         return method.CreateDelegate (owner.Type, new ExecutionScope (globals.ToArray ()));
249                 }
250
251                 static string GenerateName ()
252                 {
253                         lock (mlock) {
254                                 return "lambda_method-" + (method_count++);
255                         }
256                 }
257         }
258
259 #if !NET_2_1
260         class DebugEmitContext : DynamicEmitContext {
261
262                 public DebugEmitContext (LambdaExpression lambda)
263                         : base (lambda)
264                 {
265                         var name = Method.Name;
266                         var file_name = name + ".dll";
267
268                         var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (
269                                 new AssemblyName (name), AssemblyBuilderAccess.RunAndSave, Path.GetTempPath ());
270
271                         var type = assembly.DefineDynamicModule (file_name, file_name).DefineType ("Linq", TypeAttributes.Public);
272
273                         var method = type.DefineMethod (name, MethodAttributes.Public | MethodAttributes.Static, return_type, param_types);
274                         ig = method.GetILGenerator ();
275
276                         owner.Emit (this);
277
278                         type.CreateType ();
279                         assembly.Save (file_name);
280                 }
281         }
282 #endif
283 }