2008-01-22 Miguel de Icaza <miguel@novell.com>
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / LambdaExpression.cs
1 //
2 // LambdaExpression.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //   Miguel de Icaza (miguel@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.Reflection;
34 using System.Reflection.Emit;
35 using System.Threading;
36
37 namespace System.Linq.Expressions {
38
39         internal class EmitContext {
40                 internal LambdaExpression Owner;
41                 internal Type [] ParamTypes;
42                 internal DynamicMethod Method;
43                 internal ILGenerator ig;
44
45                 static object mlock = new object ();
46                 internal static int method_count;
47                 static string GenName ()
48                 {
49                         lock (mlock){
50                                 return "<LINQ-" + method_count++ + ">";
51                         }
52                 }
53                 
54                 public EmitContext (LambdaExpression owner)
55                 {
56                         Owner = owner;
57
58                         ParamTypes = new Type [Owner.parameters.Count];
59                         for (int i = 0; i < Owner.parameters.Count; i++)
60                                 ParamTypes [i] = Owner.parameters [i].Type;
61
62                         //
63                         // We probably want to use the 3.5 new API calls to associate
64                         // the method with the "sandboxed" Assembly, instead am currently
65                         // dumping these types in this class
66                         //
67                         Type owner_of_code = typeof (EmitContext);
68
69
70                         //
71                         // FIXME: Need to force this to be verifiable, see:
72                         // https://bugzilla.novell.com/show_bug.cgi?id=355005
73                         //
74                         string name = GenName ();
75                         Method = new DynamicMethod (name, Owner.Type, ParamTypes, owner_of_code);
76                         
77                         ig = Method.GetILGenerator ();
78                 }
79
80                 internal Delegate CreateDelegate ()
81                 {
82                         return Method.CreateDelegate (Owner.delegate_type);                     
83                 }
84
85                 internal int GetParameterPosition (ParameterExpression p)
86                 {
87                         ReadOnlyCollection<ParameterExpression> pars = Owner.Parameters;
88                         
89                         for (int i = 0; i < pars.Count; i++){
90                                 if (pars [i] == p)
91                                         return i;
92                         }
93                         throw new InvalidOperationException ("Parameter not in scope");
94                 }
95         }
96         
97         public class LambdaExpression : Expression {
98
99                 //
100                 // LambdaExpression parameters
101                 //
102                 Expression body;
103                 internal ReadOnlyCollection<ParameterExpression> parameters;
104                 internal Type delegate_type;
105
106                 // This is set during compilation
107                 Delegate lambda_delegate;
108
109                 static bool CanAssign (Type target, Type source)
110                 {
111                         // This catches object and value type mixage, type compatibility is handled later
112                         if (target.IsValueType ^ source.IsValueType)
113                                 return false;
114                         
115                         return target.IsAssignableFrom (source);
116                 }
117                                 
118                 internal LambdaExpression (Type delegateType, Expression body, ReadOnlyCollection<ParameterExpression> parameters)
119                         : base (ExpressionType.Lambda, body.Type)
120                 {
121                         if (!delegateType.IsSubclassOf (typeof (System.Delegate))){
122                                 throw new ArgumentException ("delegateType");
123                         }
124                         
125                         MethodInfo [] invokes = delegateType.GetMethods (BindingFlags.Instance | BindingFlags.Public);
126                         MethodInfo invoke = null;
127                         foreach (MethodInfo m in invokes){
128                                 if (m.Name == "Invoke"){
129                                         invoke = m;
130                                         break;
131                                 }
132                         }
133                         if (invoke == null)
134                                 throw new ArgumentException ("delegate must contain an Invoke method", "delegateType");
135
136                         ParameterInfo [] invoke_parameters = invoke.GetParameters ();
137                         if (invoke_parameters.Length != parameters.Count)
138                                 throw new ArgumentException ("Different number of arguments in delegate {0}", "delegateType");
139
140                         for (int i = 0; i < invoke_parameters.Length; i++){
141                                 if (!CanAssign (parameters [i].Type, invoke_parameters [i].ParameterType))
142                                         throw new ArgumentException (String.Format ("Can not assign a {0} to a {1}", invoke_parameters [i].ParameterType, parameters [i].Type));
143                         }
144
145                         if (!CanAssign (invoke.ReturnType, body.Type))
146                                 throw new ArgumentException (String.Format ("body type {0} can not be assigned to {1}", body.Type, invoke.ReturnType));
147                         
148                         this.body = body;
149                         this.parameters = parameters;
150                         delegate_type = delegateType;
151                 }
152                 
153                 public Expression Body {
154                         get { return body; }
155                 }
156
157                 public ReadOnlyCollection<ParameterExpression> Parameters {
158                         get { return parameters; }
159                 }
160
161                 internal override void Emit (EmitContext ec)
162                 {
163                         throw new NotImplementedException ();
164                 }
165                 
166                 public Delegate Compile ()
167                 {
168                         if (lambda_delegate == null){
169                                 EmitContext ec = new EmitContext (this);
170                                 
171                                 body.Emit (ec);
172                                 ec.ig.Emit (OpCodes.Ret);
173
174                                 if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null){
175                                         string fname = "linq" + (EmitContext.method_count-1) + ".dll";
176                                         AssemblyBuilder ab = Thread.GetDomain ().DefineDynamicAssembly (
177                                                 new AssemblyName (fname), AssemblyBuilderAccess.RunAndSave, "/tmp");
178                                         
179                                         ModuleBuilder b = ab.DefineDynamicModule (fname, fname);
180                                         TypeBuilder tb = b.DefineType ("LINQ", TypeAttributes.Public);
181                                         MethodBuilder mb = tb.DefineMethod ("GeneratedMethod", MethodAttributes.Static, Type, ec.ParamTypes);
182                                         ec.ig = mb.GetILGenerator ();
183                                         
184                                         body.Emit (ec);
185                                         ec.ig.Emit (OpCodes.Ret);
186                                         
187                                         tb.CreateType ();
188                                         ab.Save (fname);
189                                 }
190                                 
191                                 lambda_delegate = ec.CreateDelegate ();
192                         }
193                         return lambda_delegate;
194                 }
195         }
196 }