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