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