2008-01-22 Jb Evain <jbevain@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                         int position = Owner.Parameters.IndexOf (p);
88                         if (position == -1)
89                                 throw new InvalidOperationException ("Parameter not in scope");
90
91                         return position;
92                 }
93         }
94
95         public class LambdaExpression : Expression {
96
97                 //
98                 // LambdaExpression parameters
99                 //
100                 Expression body;
101                 internal ReadOnlyCollection<ParameterExpression> parameters;
102                 internal Type delegate_type;
103
104                 // This is set during compilation
105                 Delegate lambda_delegate;
106
107                 static bool CanAssign (Type target, Type source)
108                 {
109                         // This catches object and value type mixage, type compatibility is handled later
110                         if (target.IsValueType ^ source.IsValueType)
111                                 return false;
112                         
113                         return target.IsAssignableFrom (source);
114                 }
115                                 
116                 internal LambdaExpression (Type delegateType, Expression body, ReadOnlyCollection<ParameterExpression> parameters)
117                         : base (ExpressionType.Lambda, body.Type)
118                 {
119                         if (!delegateType.IsSubclassOf (typeof (System.Delegate)))
120                                 throw new ArgumentException ("delegateType");
121
122                         var invoke = delegateType.GetMethod ("Invoke", BindingFlags.Instance | BindingFlags.Public);
123                         if (invoke == null)
124                                 throw new ArgumentException ("delegate must contain an Invoke method", "delegateType");
125
126                         var invoke_parameters = invoke.GetParameters ();
127                         if (invoke_parameters.Length != parameters.Count)
128                                 throw new ArgumentException ("Different number of arguments in delegate {0}", "delegateType");
129
130                         for (int i = 0; i < invoke_parameters.Length; i++){
131                                 if (!CanAssign (parameters [i].Type, invoke_parameters [i].ParameterType))
132                                         throw new ArgumentException (String.Format ("Can not assign a {0} to a {1}", invoke_parameters [i].ParameterType, parameters [i].Type));
133                         }
134
135                         if (!CanAssign (invoke.ReturnType, body.Type))
136                                 throw new ArgumentException (String.Format ("body type {0} can not be assigned to {1}", body.Type, invoke.ReturnType));
137                         
138                         this.body = body;
139                         this.parameters = parameters;
140                         delegate_type = delegateType;
141                 }
142                 
143                 public Expression Body {
144                         get { return body; }
145                 }
146
147                 public ReadOnlyCollection<ParameterExpression> Parameters {
148                         get { return parameters; }
149                 }
150
151                 internal override void Emit (EmitContext ec)
152                 {
153                         body.Emit (ec);
154                         ec.ig.Emit (OpCodes.Ret);
155                 }
156
157                 public Delegate Compile ()
158                 {
159                         if (lambda_delegate != null)
160                                 return lambda_delegate;
161
162                         var ec = new EmitContext (this);
163
164                         Emit (ec);
165
166                         if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null){
167                                 string fname = "linq" + (EmitContext.method_count-1) + ".dll";
168                                 AssemblyBuilder ab = Thread.GetDomain ().DefineDynamicAssembly (
169                                         new AssemblyName (fname), AssemblyBuilderAccess.RunAndSave, "/tmp");
170                                 
171                                 ModuleBuilder b = ab.DefineDynamicModule (fname, fname);
172                                 TypeBuilder tb = b.DefineType ("LINQ", TypeAttributes.Public);
173                                 MethodBuilder mb = tb.DefineMethod ("GeneratedMethod", MethodAttributes.Static, Type, ec.ParamTypes);
174                                 ec.ig = mb.GetILGenerator ();
175
176                                 Emit (ec);
177
178                                 tb.CreateType ();
179                                 ab.Save (fname);
180                         }
181                         
182                         lambda_delegate = ec.CreateDelegate ();
183                         return lambda_delegate;
184                 }
185         }
186 }