2009-09-11 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / dynamic.cs
1 //
2 // dynamic.cs: support for dynamic expressions
3 //
4 // Authors: Marek Safar (marek.safar@gmail.com)
5 //
6 // Dual licensed under the terms of the MIT X11 or GNU GPL
7 //
8 // Copyright 2009 Novell, Inc
9 //
10
11 using System;
12 using System.Collections;
13
14 #if NET_4_0
15 using System.Dynamic;
16 #endif
17
18 namespace Mono.CSharp
19 {
20         class DynamicTypeExpr : TypeExpr
21         {
22                 public DynamicTypeExpr (Location loc)
23                 {
24                         this.loc = loc;
25
26                         type = InternalType.Dynamic;
27                         eclass = ExprClass.Type;
28                 }
29
30                 public override bool CheckAccessLevel (IMemberContext ds)
31                 {
32                         return true;
33                 }
34
35                 protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec)
36                 {
37                         return this;
38                 }
39         }
40
41 #if NET_4_0
42         public class RuntimeValueExpression : Expression
43         {
44                 readonly DynamicMetaObject obj;
45
46                 public RuntimeValueExpression (DynamicMetaObject obj)
47                 {
48                         this.obj = obj;
49                         this.type = obj.RuntimeType;
50                         this.eclass = ExprClass.Value;
51                 }
52
53                 public override Expression CreateExpressionTree (ResolveContext ec)
54                 {
55                         throw new NotImplementedException ();
56                 }
57
58                 public override Expression DoResolve (ResolveContext ec)
59                 {
60                         return this;
61                 }
62
63                 public override void Emit (EmitContext ec)
64                 {
65                         throw new NotImplementedException ();
66                 }
67
68                 public override System.Linq.Expressions.Expression MakeExpression ()
69                 {
70                         if (obj.Expression.Type != type)
71                                 return System.Linq.Expressions.Expression.Convert (obj.Expression, type);
72
73                         return obj.Expression;
74                 }
75         }
76 #endif
77
78         interface IDynamicBinder
79         {
80                 Expression CreateCallSiteBinder (ResolveContext ec, Arguments args);
81         }
82
83         class DynamicExpressionStatement : ExpressionStatement
84         {
85                 class StaticDataClass : CompilerGeneratedClass
86                 {
87                         public StaticDataClass ()
88                                 : base (new RootDeclSpace (new NamespaceEntry (null, null, null)),
89                                         new MemberName (CompilerGeneratedClass.MakeName (null, "c", "DynamicSites", 0)),
90                                         Modifiers.INTERNAL | Modifiers.STATIC)
91                         {
92                                 ModFlags &= ~Modifiers.SEALED;
93                         }
94                 }
95
96                 static StaticDataClass global_site_container;
97                 static int field_counter;
98                 static int container_counter;
99
100                 readonly Arguments arguments;
101                 protected IDynamicBinder binder;
102
103                 public DynamicExpressionStatement (IDynamicBinder binder, Arguments args, Location loc)
104                 {
105                         this.binder = binder;
106                         this.arguments = args;
107                         this.loc = loc;
108                 }
109
110                 public Arguments Arguments {
111                         get {
112                                 return arguments;
113                         }
114                 }
115
116                 static TypeContainer CreateSiteContainer ()
117                 {
118                         if (global_site_container == null) {
119                                 global_site_container = new StaticDataClass ();
120                                 RootContext.ToplevelTypes.AddCompilerGeneratedClass (global_site_container);
121                                 global_site_container.DefineType ();
122                                 global_site_container.Define ();
123 //                              global_site_container.EmitType ();
124
125                                 RootContext.RegisterCompilerGeneratedType (global_site_container.TypeBuilder);
126                         }
127
128                         return global_site_container;
129                 }
130
131                 static Field CreateSiteField (FullNamedExpression type)
132                 {
133                         TypeContainer site_container = CreateSiteContainer ();
134                         Field f = new Field (site_container, type, Modifiers.PUBLIC | Modifiers.STATIC,
135                                 new MemberName ("Site" +  field_counter++), null);
136                         f.Define ();
137
138                         site_container.AddField (f);
139                         return f;
140                 }
141
142                 public override Expression CreateExpressionTree (ResolveContext ec)
143                 {
144                         throw new NotImplementedException ();
145                 }
146
147                 public override Expression DoResolve (ResolveContext ec)
148                 {
149                         if (TypeManager.call_site_type == null)
150                                 TypeManager.call_site_type = TypeManager.CoreLookupType (ec.Compiler,
151                                         "System.Runtime.CompilerServices", "CallSite", Kind.Class, true);
152
153                         if (TypeManager.generic_call_site_type == null)
154                                 TypeManager.generic_call_site_type = TypeManager.CoreLookupType (ec.Compiler,
155                                         "System.Runtime.CompilerServices", "CallSite`1", Kind.Class, true);
156
157                         eclass = ExprClass.Value;
158
159                         if (type == null)
160                                 type = InternalType.Dynamic;
161
162                         return this;
163                 }
164
165                 public override void Emit (EmitContext ec)
166                 {
167                         EmitCall (ec, false);
168                 }
169
170                 public override void EmitStatement (EmitContext ec)
171                 {
172                         EmitCall (ec, true);
173                 }
174
175                 void EmitCall (EmitContext ec, bool isStatement)
176                 {
177                         int dyn_args_count = arguments == null ? 0 : arguments.Count;
178                         TypeExpr site_type = CreateSiteType (RootContext.ToplevelTypes.Compiler, isStatement, dyn_args_count);
179                         FieldExpr site_field_expr = new FieldExpr (CreateSiteField (site_type).FieldBuilder, loc);
180
181                         SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
182
183                         BlockContext bc = new BlockContext (ec.MemberContext, null, TypeManager.void_type);
184                         if (ec.HasSet (EmitContext.Options.CheckedScope))
185                                 bc.Set (ResolveContext.Options.CheckedScope);
186
187                         Arguments args = new Arguments (1);
188                         args.Add (new Argument (binder.CreateCallSiteBinder (bc, arguments)));
189                         StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (site_type, "Create"), args)));
190                         if (s.Resolve (bc)) {
191                                 Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc)), s, loc);
192                                 init.Emit (ec);
193                         }
194
195                         args = new Arguments (1 + dyn_args_count);
196                         args.Add (new Argument (site_field_expr));
197                         if (arguments != null) {
198                                 foreach (Argument a in arguments) {
199                                         if (a is NamedArgument) {
200                                                 // Name is not valid in this context
201                                                 args.Add (new Argument (a.Expr, a.ArgType));
202                                                 continue;
203                                         }
204
205                                         args.Add (a);
206                                 }
207                         }
208
209                         ResolveContext rc = new ResolveContext (ec.MemberContext);
210                         Expression target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (rc), args, loc).Resolve (rc);
211                         if (target != null)
212                                 target.Emit (ec);
213
214                         SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
215                 }
216
217                 public static MemberAccess GetBinderNamespace (Location loc)
218                 {
219                         return new MemberAccess (new MemberAccess (
220                                 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "Microsoft", loc), "CSharp", loc), "RuntimeBinder", loc);
221                 }
222
223                 TypeExpr CreateSiteType (CompilerContext ctx, bool isStatement, int dyn_args_count)
224                 {
225                         int default_args = isStatement ? 1 : 2;
226
227                         bool has_ref_out_argument = false;
228                         FullNamedExpression[] targs = new FullNamedExpression[dyn_args_count + default_args];
229                         targs [0] = new TypeExpression (TypeManager.call_site_type, loc);
230                         for (int i = 0; i < dyn_args_count; ++i) {
231                                 Type arg_type;
232                                 Argument a = arguments [i];
233                                 if (a.Type == TypeManager.null_type)
234                                         arg_type = TypeManager.object_type;
235                                 else
236                                         arg_type = TypeManager.TypeToReflectionType (a.Type);
237
238                                 if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref)
239                                         has_ref_out_argument = true;
240
241                                 targs [i + 1] = new TypeExpression (arg_type, loc);
242                         }
243
244                         TypeExpr del_type = null;
245                         if (!has_ref_out_argument) {
246                                 string d_name = isStatement ? "Action`" : "Func`";
247
248                                 Type t = TypeManager.CoreLookupType (ctx, "System", d_name + (dyn_args_count + default_args), Kind.Delegate, false);
249                                 if (t != null) {
250                                         if (!isStatement)
251                                                 targs[targs.Length - 1] = new TypeExpression (TypeManager.TypeToReflectionType (type), loc);
252
253                                         del_type = new GenericTypeExpr (t, new TypeArguments (targs), loc);
254                                 }
255                         }
256
257                         // No appropriate predefined delegate found
258                         if (del_type == null) {
259                                 Type rt = isStatement ? TypeManager.void_type : type;
260                                 Parameter[] p = new Parameter [dyn_args_count + 1];
261                                 p[0] = new Parameter (targs [0], "p0", Parameter.Modifier.NONE, null, loc);
262
263                                 for (int i = 1; i < dyn_args_count + 1; ++i)
264                                         p[i] = new Parameter (targs[i], "p" + i.ToString ("X"), arguments[i - 1].Modifier, null, loc);
265
266                                 TypeContainer parent = CreateSiteContainer ();
267                                 Delegate d = new Delegate (parent.NamespaceEntry, parent, new TypeExpression (rt, loc),
268                                         Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED,
269                                         new MemberName ("Container" + container_counter++.ToString ("X")),
270                                         new ParametersCompiled (p), null);
271
272                                 d.DefineType ();
273                                 d.Define ();
274
275                                 parent.AddDelegate (d);
276                                 del_type = new TypeExpression (d.TypeBuilder, loc);
277                         }
278
279                         TypeExpr site_type = new GenericTypeExpr (TypeManager.generic_call_site_type, new TypeArguments (del_type), loc);
280                         return site_type;
281                 }
282
283                 public static void Reset ()
284                 {
285                         global_site_container = null;
286                         field_counter = container_counter = 0;
287                 }
288         }
289
290         //
291         // Dynamic member access compound assignment for events
292         //
293         class DynamicEventCompoundAssign : DynamicExpressionStatement, IDynamicBinder
294         {
295                 string name;
296                 ExpressionStatement assignment;
297                 ExpressionStatement invoke;
298
299                 public DynamicEventCompoundAssign (string name, Arguments args, ExpressionStatement assignment, ExpressionStatement invoke, Location loc)
300                         : base (null, args, loc)
301                 {
302                         this.name = name;
303                         this.assignment = assignment;
304                         this.invoke = invoke;
305                         base.binder = this;
306
307                         // Used by += or -= only
308                         type = TypeManager.bool_type;
309                 }
310
311                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
312                 {
313                         Arguments binder_args = new Arguments (2);
314                         MemberAccess binder = GetBinderNamespace (loc);
315
316                         binder_args.Add (new Argument (new StringLiteral (name, loc)));
317                         binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
318
319                         return new New (new MemberAccess (binder, "CSharpIsEventBinder", loc), binder_args, loc);
320                 }
321
322                 public override void EmitStatement (EmitContext ec)
323                 {
324                         Statement cond = new If (
325                                 new Binary (Binary.Operator.Equality, this, new BoolLiteral (true, loc)),
326                                 new StatementExpression (invoke),
327                                 new StatementExpression (assignment),
328                                 loc);
329                         cond.Emit (ec);
330                 }
331         }
332
333         class DynamicConversion : DynamicExpressionStatement, IDynamicBinder
334         {
335                 bool is_explicit;
336
337                 public DynamicConversion (Type targetType, bool isExplicit, Arguments args, Location loc)
338                         : base (null, args, loc)
339                 {
340                         type = targetType;
341                         is_explicit = isExplicit;
342                         base.binder = this;
343                 }
344
345                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
346                 {
347                         Arguments binder_args = new Arguments (2);
348                         MemberAccess binder = GetBinderNamespace (loc);
349
350                         binder_args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
351                         binder_args.Add (new Argument (new MemberAccess (new MemberAccess (binder, "CSharpConversionKind", loc),
352                                 is_explicit ? "ExplicitConversion" : "ImplicitConversion", loc)));
353                         binder_args.Add (new Argument (new BoolLiteral (ec.HasSet (ResolveContext.Options.CheckedScope), loc)));
354                                 
355                         return new New (new MemberAccess (binder, "CSharpConvertBinder", loc), binder_args, loc);
356                 }
357         }
358
359         class DynamicIndexBinder : DynamicExpressionStatement, IDynamicBinder, IAssignMethod
360         {
361                 readonly bool isSet;
362
363                 public DynamicIndexBinder (bool isSet, Arguments args, Location loc)
364                         : base (null, args, loc)
365                 {
366                         base.binder = this;
367                         this.isSet = isSet;
368                 }
369
370                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
371                 {
372                         Arguments binder_args = new Arguments (2);
373                         MemberAccess binder = GetBinderNamespace (loc);
374
375                         binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
376                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc)));
377
378                         return new New (new MemberAccess (binder, isSet ? "CSharpSetIndexBinder" : "CSharpGetIndexBinder", loc), binder_args, loc);
379                 }
380
381                 #region IAssignMethod Members
382
383                 public void Emit (EmitContext ec, bool leave_copy)
384                 {
385                         throw new NotImplementedException ();
386                 }
387
388                 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
389                 {
390                         EmitStatement (ec);
391                 }
392
393                 #endregion
394         }
395
396         class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder
397         {
398                 ATypeNameExpression member;
399
400                 public DynamicInvocation (ATypeNameExpression member, Arguments args, Location loc)
401                         : base (null, args, loc)
402                 {
403                         base.binder = this;
404                         this.member = member;
405                 }
406
407                 public DynamicInvocation (ATypeNameExpression member, Arguments args, Type type, Location loc)
408                         : this (member, args, loc)
409                 {
410                         // When a return type is known not to be dynamic
411                         this.type = type;
412                 }
413
414                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
415                 {
416                         Arguments binder_args = new Arguments (member != null ? 5 : 3);
417                         MemberAccess binder = GetBinderNamespace (loc);
418                         bool is_member_access = member is MemberAccess;
419
420                         string call_flags;
421                         if (!is_member_access && member is SimpleName) {
422                                 call_flags = "SimpleNameCall";
423                                 is_member_access = true;
424                         } else {
425                                 call_flags = "None";
426                         }
427
428                         binder_args.Add (new Argument (new MemberAccess (new MemberAccess (binder, "CSharpCallFlags", loc), call_flags, loc)));
429
430                         if (is_member_access)
431                                 binder_args.Add (new Argument (new StringLiteral (member.Name, member.Location)));
432
433                         binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
434
435                         if (member != null && member.HasTypeArguments) {
436                                 TypeArguments ta = member.TypeArguments;
437                                 if (ta.Resolve (ec)) {
438                                         ArrayList targs = new ArrayList (ta.Count);
439                                         foreach (Type t in ta.Arguments)
440                                                 targs.Add (new TypeOf (new TypeExpression (t, loc), loc));
441
442                                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", targs, loc)));
443                                 }
444                         } else if (is_member_access) {
445                                 binder_args.Add (new Argument (new NullLiteral (loc)));
446                         }
447
448                         Expression real_args;
449                         if (args == null) {
450                                 // Cannot be null because .NET trips over
451                                 real_args = new ArrayCreation (new MemberAccess (binder, "CSharpArgumentInfo", loc), "[]", new ArrayList (0), loc);
452                         } else {
453                                 real_args = new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc);
454                         }
455
456                         binder_args.Add (new Argument (real_args));
457
458                         return new New (new MemberAccess (binder,
459                                 is_member_access ? "CSharpInvokeMemberBinder" : "CSharpInvokeBinder", loc), binder_args, loc);
460                 }
461         }
462
463         class DynamicMemberBinder : DynamicExpressionStatement, IDynamicBinder, IAssignMethod
464         {
465                 readonly bool isSet;
466                 readonly string name;
467
468                 public DynamicMemberBinder (bool isSet, string name, Arguments args, Location loc)
469                         : base (null, args, loc)
470                 {
471                         base.binder = this;
472                         this.isSet = isSet;
473                         this.name = name;
474                 }
475
476                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
477                 {
478                         Arguments binder_args = new Arguments (3);
479                         MemberAccess binder = GetBinderNamespace (loc);
480
481                         binder_args.Add (new Argument (new StringLiteral (name, loc)));
482                         binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
483                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc)));
484
485                         return new New (new MemberAccess (binder, isSet ? "CSharpSetMemberBinder" : "CSharpGetMemberBinder", loc), binder_args, loc);
486                 }
487
488                 #region IAssignMethod Members
489
490                 public void Emit (EmitContext ec, bool leave_copy)
491                 {
492                         throw new NotImplementedException ();
493                 }
494
495                 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
496                 {
497                         EmitStatement (ec);
498                 }
499
500                 #endregion
501         }
502
503         class DynamicUnaryConversion : DynamicExpressionStatement, IDynamicBinder
504         {
505                 string name;
506
507                 public DynamicUnaryConversion (string name, Arguments args, Location loc)
508                         : base (null, args, loc)
509                 {
510                         this.name = name;
511                         base.binder = this;
512                         if (name == "IsTrue" || name == "IsFalse")
513                                 type = TypeManager.bool_type;
514                 }
515
516                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
517                 {
518                         Arguments binder_args = new Arguments (3);
519
520                         MemberAccess sle = new MemberAccess (new MemberAccess (
521                                 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc);
522
523                         MemberAccess binder = GetBinderNamespace (loc);
524
525                         binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), name, loc)));
526                         binder_args.Add (new Argument (new BoolLiteral (ec.HasSet (ResolveContext.Options.CheckedScope), loc)));
527                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc)));
528
529                         return new New (new MemberAccess (binder, "CSharpUnaryOperationBinder", loc), binder_args, loc);
530                 }
531         }
532 }