2010-05-27 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / class / Microsoft.CSharp / Microsoft.CSharp.RuntimeBinder / CSharpBinder.cs
1 //
2 // CSharpBinder.cs
3 //
4 // Authors:
5 //      Marek Safar  <marek.safar@gmail.com>
6 //
7 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Dynamic;
31 using System.Linq.Expressions;
32 using Compiler = Mono.CSharp;
33 using System.Reflection;
34 using System.Collections.Generic;
35
36 namespace Microsoft.CSharp.RuntimeBinder
37 {
38         class CSharpBinder
39         {
40                 static ConstructorInfo binder_exception_ctor;
41                 static object compiler_initializer = new object ();
42                 static object resolver = new object ();
43
44                 DynamicMetaObjectBinder binder;
45                 Compiler.Expression expr;
46                 BindingRestrictions restrictions;
47                 DynamicMetaObject errorSuggestion;
48
49                 public CSharpBinder (DynamicMetaObjectBinder binder, Compiler.Expression expr, DynamicMetaObject errorSuggestion)
50                 {
51                         this.binder = binder;
52                         this.expr = expr;
53                         this.restrictions = BindingRestrictions.Empty;
54                         this.errorSuggestion = errorSuggestion;
55                 }
56
57                 public Compiler.ResolveContext.Options ResolveOptions { get; set; }
58
59                 public void AddRestrictions (DynamicMetaObject arg)
60                 {
61                         restrictions = restrictions.Merge (CreateRestrictionsOnTarget (arg));
62                 }
63
64                 public void AddRestrictions (DynamicMetaObject[] args)
65                 {
66                         restrictions = restrictions.Merge (CreateRestrictionsOnTarget (args));
67                 }
68
69                 public DynamicMetaObject Bind (Type callingType, DynamicMetaObject target)
70                 {
71                         if (target.Value == null) {
72                                 if (errorSuggestion != null)
73                                         return errorSuggestion;
74
75                                 var ex = CreateBinderException ("Cannot perform member binding on `null' value");
76                                 return new DynamicMetaObject (ex, restrictions);
77                         }
78
79                         return Bind (callingType);
80                 }
81
82                 public DynamicMetaObject Bind (Type callingType)
83                 {
84                         var ctx = CreateDefaultCompilerContext ();
85
86                         InitializeCompiler (ctx);
87
88                         Expression res;
89                         try {
90                                 var rc = new Compiler.ResolveContext (new RuntimeBinderContext (ctx, TypeImporter.Import (callingType)), ResolveOptions);
91
92                                 // Static typemanager and internal caches are not thread-safe
93                                 lock (resolver) {
94                                         expr = expr.Resolve (rc, Compiler.ResolveFlags.VariableOrValue);
95                                 }
96
97                                 if (expr == null)
98                                         throw new RuntimeBinderInternalCompilerException ("Expression resolved to null");
99
100                                 res = expr.MakeExpression (new Compiler.BuilderContext ());
101                         } catch (RuntimeBinderException e) {
102                                 if (errorSuggestion != null)
103                                         return errorSuggestion;
104
105                                 res = CreateBinderException (e.Message);
106                         } catch (Exception) {
107                                 if (errorSuggestion != null)
108                                         return errorSuggestion;
109
110                                 throw;
111                         }
112
113                         return new DynamicMetaObject (res, restrictions);
114                 }
115
116                 Expression CreateBinderException (string message)
117                 {
118                         if (binder_exception_ctor == null)
119                                 binder_exception_ctor = typeof (RuntimeBinderException).GetConstructor (new[] { typeof (string) });
120
121                         //
122                         // Uses target type to keep expressions composition working
123                         //
124                         return Expression.Throw (Expression.New (binder_exception_ctor, Expression.Constant (message)), binder.ReturnType);
125                 }
126
127                 //
128                 // Creates mcs expression from dynamic method object
129                 //
130                 public static Compiler.Expression CreateCompilerExpression (CSharpArgumentInfo info, DynamicMetaObject value)
131                 {
132                         if (value.Value == null) {
133                                 if (value.LimitType == typeof (object))
134                                         return new Compiler.NullLiteral (Compiler.Location.Null);
135
136                                 InitializeCompiler (null);
137                                 return Compiler.Constant.CreateConstantFromValue (TypeImporter.Import (value.LimitType), null, Compiler.Location.Null);
138                         }
139
140                         bool is_compile_time;
141
142                         if (info != null) {
143                                 if ((info.Flags & CSharpArgumentInfoFlags.Constant) != 0) {
144                                         InitializeCompiler (null);
145                                         return Compiler.Constant.CreateConstantFromValue (TypeImporter.Import (value.LimitType), value.Value, Compiler.Location.Null);
146                                 }
147
148                                 if ((info.Flags & CSharpArgumentInfoFlags.IsStaticType) != 0)
149                                         return new Compiler.TypeExpression (TypeImporter.Import ((Type) value.Value), Compiler.Location.Null);
150
151                                 is_compile_time = (info.Flags & CSharpArgumentInfoFlags.UseCompileTimeType) != 0;
152                         } else {
153                                 is_compile_time = false;
154                         }
155
156                         return new Compiler.RuntimeValueExpression (value, TypeImporter.Import (is_compile_time ? value.LimitType : value.RuntimeType));
157                 }
158
159                 public static Compiler.Arguments CreateCompilerArguments (IEnumerable<CSharpArgumentInfo> info, DynamicMetaObject[] args)
160                 {
161                         var res = new Compiler.Arguments (args.Length);
162                         int pos = 0;
163
164                         // enumerates over args
165                         foreach (var item in info) {
166                                 var expr = CreateCompilerExpression (item, args [pos++]);
167                                 if (item.IsNamed) {
168                                         res.Add (new Compiler.NamedArgument (item.Name, Compiler.Location.Null, expr));
169                                 } else {
170                                         res.Add (new Compiler.Argument (expr, item.ArgumentModifier));
171                                 }
172
173                                 if (pos == args.Length)
174                                         break;
175                         }
176
177                         return res;
178                 }
179
180                 public static Compiler.CompilerContext CreateDefaultCompilerContext ()
181                 {
182                         return new Compiler.CompilerContext (
183                                 new Compiler.Report (ErrorPrinter.Instance) {
184                                         WarningLevel = 0
185                                 }) {
186                                         IsRuntimeBinder = true
187                                 };
188                 }
189
190                 static BindingRestrictions CreateRestrictionsOnTarget (DynamicMetaObject arg)
191                 {
192                         return arg.HasValue && arg.Value == null ?
193                                 BindingRestrictions.GetInstanceRestriction (arg.Expression, null) :
194                                 BindingRestrictions.GetTypeRestriction (arg.Expression, arg.LimitType);
195                 }
196
197                 public static BindingRestrictions CreateRestrictionsOnTarget (DynamicMetaObject[] args)
198                 {
199                         if (args.Length == 0)
200                                 return BindingRestrictions.Empty;
201
202                         var res = CreateRestrictionsOnTarget (args[0]);
203                         for (int i = 1; i < args.Length; ++i)
204                                 res = res.Merge (CreateRestrictionsOnTarget (args[i]));
205
206                         return res;
207                 }
208
209                 public static void InitializeCompiler (Compiler.CompilerContext ctx)
210                 {
211                         if (TypeImporter.Predefined == null)
212                                 return;
213
214                         lock (compiler_initializer) {
215                                 if (TypeImporter.Predefined == null)
216                                         return;
217
218                                 // I don't think dynamically loaded assemblies can be used as dynamic
219                                 // expression without static type to be loaded first
220                                 // AppDomain.CurrentDomain.AssemblyLoad += (sender, e) => { throw new NotImplementedException (); };
221
222                                 // Import all currently loaded assemblies
223                                 var ns = Compiler.GlobalRootNamespace.Instance;
224                                 foreach (System.Reflection.Assembly a in AppDomain.CurrentDomain.GetAssemblies ()) {
225                                         ns.AddAssemblyReference (a);
226                                         ns.ImportAssembly (a);
227                                 }
228
229                                 if (ctx == null)
230                                         ctx = CreateDefaultCompilerContext ();
231
232                                 Compiler.TypeManager.InitCoreTypes (ctx, TypeImporter.Predefined);
233                                 TypeImporter.Predefined = null;
234
235                                 Compiler.TypeManager.InitOptionalCoreTypes (ctx);
236                         }
237                 }
238         }
239
240         static class TypeImporter
241         {
242                 static object lock_object;
243                 public static IList<Compiler.PredefinedTypeSpec> Predefined;
244
245                 static TypeImporter ()
246                 {
247                         lock_object = new object ();
248                         Predefined = Compiler.TypeManager.InitCoreTypes ();
249                         Compiler.Import.Initialize ();
250                 }
251
252                 public static Compiler.TypeSpec Import (Type type)
253                 {
254                         lock (lock_object) {
255                                 return Compiler.Import.ImportType (type);
256                         }
257                 }
258         }
259 }