ifdef out DebugContext on net_2_1
[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.Generic;
32 using System.IO;
33 using System.Linq;
34 using System.Reflection;
35 using System.Reflection.Emit;
36
37 namespace System.Linq.Expressions {
38
39         abstract class EmitContext {
40
41                 protected LambdaExpression owner;
42                 protected Type [] param_types;
43                 protected Type return_type;
44
45                 public ILGenerator ig;
46
47                 protected EmitContext (LambdaExpression lambda)
48                 {
49                         this.owner = lambda;
50
51                         param_types = owner.Parameters.Select (p => p.Type).ToArray ();
52                         return_type = owner.GetReturnType ();
53                 }
54
55                 public static EmitContext Create (LambdaExpression lambda)
56                 {
57 #if !NET_2_1
58                         if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null)
59                                 return new DebugEmitContext (lambda);
60 #endif
61                         return new DynamicEmitContext (lambda);
62                 }
63
64                 public int GetParameterPosition (ParameterExpression p)
65                 {
66                         int position = owner.Parameters.IndexOf (p);
67                         if (position == -1)
68                                 throw new InvalidOperationException ("Parameter not in scope");
69
70                         return position;
71                 }
72
73                 public abstract Delegate CreateDelegate ();
74
75                 public void Emit (Expression expression)
76                 {
77                         expression.Emit (this);
78                 }
79
80                 public LocalBuilder EmitStored (Expression expression)
81                 {
82                         var local = ig.DeclareLocal (expression.Type);
83                         expression.Emit (this);
84                         ig.Emit (OpCodes.Stloc, local);
85
86                         return local;
87                 }
88
89                 public void EmitLoad (Expression expression)
90                 {
91                         if (expression.Type.IsValueType) {
92                                 var local = EmitStored (expression);
93                                 ig.Emit (OpCodes.Ldloca, local);
94                         } else
95                                 expression.Emit (this);
96                 }
97
98                 public void EmitLoad (LocalBuilder local)
99                 {
100                         ig.Emit (OpCodes.Ldloc, local);
101                 }
102
103                 public void EmitCall (LocalBuilder local, IEnumerable<Expression> arguments, MethodInfo method)
104                 {
105                         EmitLoad (local);
106                         EmitCollection (arguments);
107                         EmitCall (method);
108                 }
109
110                 public void EmitCall (Expression expression, IEnumerable<Expression> arguments, MethodInfo method)
111                 {
112                         if (expression != null)
113                                 EmitLoad (expression);
114
115                         EmitCollection (arguments);
116                         EmitCall (method);
117                 }
118
119                 public void EmitCall (MethodInfo method)
120                 {
121                         ig.Emit (
122                                 method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
123                                 method);
124                 }
125
126                 public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
127                 {
128                         foreach (var expression in collection)
129                                 expression.Emit (this);
130                 }
131
132                 public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
133                 {
134                         foreach (var initializer in initializers)
135                                 initializer.Emit (this, local);
136                 }
137
138                 public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
139                 {
140                         foreach (var binding in bindings)
141                                 binding.Emit (this, local);
142                 }
143
144                 public void EmitIsInst (Expression expression, Type candidate)
145                 {
146                         expression.Emit (this);
147
148                         if (expression.Type.IsValueType)
149                                 ig.Emit (OpCodes.Box, expression.Type);
150
151                         ig.Emit (OpCodes.Isinst, candidate);
152                 }
153         }
154
155         class DynamicEmitContext : EmitContext {
156
157                 DynamicMethod method;
158
159                 static object mlock = new object ();
160                 static int method_count;
161
162                 public DynamicMethod Method {
163                         get { return method; }
164                 }
165
166                 public DynamicEmitContext (LambdaExpression lambda)
167                         : base (lambda)
168                 {
169                         // FIXME: Need to force this to be verifiable, see:
170                         // https://bugzilla.novell.com/show_bug.cgi?id=355005
171                         method = new DynamicMethod (GenerateName (), return_type, param_types, typeof (EmitContext), true);
172                         ig = method.GetILGenerator ();
173
174                         owner.Emit (this);
175                 }
176
177                 public override Delegate CreateDelegate ()
178                 {
179                         return method.CreateDelegate (owner.Type);
180                 }
181
182                 static string GenerateName ()
183                 {
184                         lock (mlock) {
185                                 return "lambda_method-" + (method_count++);
186                         }
187                 }
188         }
189
190 #if !NET_2_1
191         class DebugEmitContext : EmitContext {
192
193                 DynamicEmitContext dynamic_context;
194
195                 public DebugEmitContext (LambdaExpression lambda)
196                         : base (lambda)
197                 {
198                         dynamic_context = new DynamicEmitContext (lambda);
199
200                         var name = dynamic_context.Method.Name;
201                         var file_name = name + ".dll";
202
203                         var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (
204                                 new AssemblyName (name), AssemblyBuilderAccess.RunAndSave, Path.GetTempPath ());
205
206                         var type = assembly.DefineDynamicModule (file_name, file_name).DefineType ("Linq", TypeAttributes.Public);
207
208                         var method = type.DefineMethod (name, MethodAttributes.Public | MethodAttributes.Static, return_type, param_types);
209                         ig = method.GetILGenerator ();
210
211                         owner.Emit (this);
212
213                         type.CreateType ();
214                         assembly.Save (file_name);
215                 }
216
217                 public override Delegate CreateDelegate ()
218                 {
219                         return dynamic_context.CreateDelegate ();
220                 }
221         }
222 #endif
223 }