2010-01-12 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / mcs / dynamic.cs
blob2f388cc4719b3b36bfa8376c0745fafa8ae7b616
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 //
11 using System;
12 using System.Collections;
14 #if NET_4_0
15 using System.Dynamic;
16 #endif
18 namespace Mono.CSharp
20 class DynamicTypeExpr : TypeExpr
22 public DynamicTypeExpr (Location loc)
24 this.loc = loc;
26 type = InternalType.Dynamic;
27 eclass = ExprClass.Type;
30 public override bool CheckAccessLevel (IMemberContext ds)
32 return true;
35 protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec)
37 return this;
42 // Expression created from runtime dynamic object value
44 public class RuntimeValueExpression : Expression, IAssignMethod
46 #if !NET_4_0
47 public class DynamicMetaObject { public Type RuntimeType; }
48 #endif
50 readonly DynamicMetaObject obj;
52 // When strongly typed expression is required
53 readonly bool typed;
55 public RuntimeValueExpression (DynamicMetaObject obj, bool typed)
57 this.obj = obj;
58 this.typed = typed;
59 this.type = obj.RuntimeType;
60 this.eclass = ExprClass.Value;
63 public override Expression CreateExpressionTree (ResolveContext ec)
65 throw new NotImplementedException ();
68 public override Expression DoResolve (ResolveContext ec)
70 return this;
73 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
75 return this;
78 public override void Emit (EmitContext ec)
80 throw new NotImplementedException ();
83 #region IAssignMethod Members
85 public void Emit (EmitContext ec, bool leave_copy)
87 throw new NotImplementedException ();
90 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
92 throw new NotImplementedException ();
95 #endregion
97 #if NET_4_0
98 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
100 if (typed && obj.Expression.Type != type)
101 return System.Linq.Expressions.Expression.Convert (obj.Expression, type);
103 return obj.Expression;
105 #endif
108 interface IDynamicBinder
110 Expression CreateCallSiteBinder (ResolveContext ec, Arguments args);
113 class DynamicExpressionStatement : ExpressionStatement
115 class StaticDataClass : CompilerGeneratedClass
117 public StaticDataClass ()
118 : base (new RootDeclSpace (new NamespaceEntry (null, null, null)),
119 new MemberName (CompilerGeneratedClass.MakeName (null, "c", "DynamicSites", 0)),
120 Modifiers.INTERNAL | Modifiers.STATIC)
122 ModFlags &= ~Modifiers.SEALED;
126 static StaticDataClass global_site_container;
127 static int field_counter;
128 static int container_counter;
130 readonly Arguments arguments;
131 protected IDynamicBinder binder;
132 Expression binder_expr;
134 public DynamicExpressionStatement (IDynamicBinder binder, Arguments args, Location loc)
136 this.binder = binder;
137 this.arguments = args;
138 this.loc = loc;
141 public Arguments Arguments {
142 get {
143 return arguments;
147 static TypeContainer CreateSiteContainer ()
149 if (global_site_container == null) {
150 global_site_container = new StaticDataClass ();
151 RootContext.ToplevelTypes.AddCompilerGeneratedClass (global_site_container);
152 global_site_container.DefineType ();
153 global_site_container.Define ();
154 // global_site_container.EmitType ();
156 RootContext.RegisterCompilerGeneratedType (global_site_container.TypeBuilder);
159 return global_site_container;
162 static Field CreateSiteField (FullNamedExpression type)
164 TypeContainer site_container = CreateSiteContainer ();
165 Field f = new Field (site_container, type, Modifiers.PUBLIC | Modifiers.STATIC,
166 new MemberName ("Site" + field_counter++), null);
167 f.Define ();
169 site_container.AddField (f);
170 return f;
173 public override Expression CreateExpressionTree (ResolveContext ec)
175 throw new NotImplementedException ();
178 public override Expression DoResolve (ResolveContext ec)
180 if (eclass != ExprClass.Invalid)
181 return this;
183 if (TypeManager.call_site_type == null)
184 TypeManager.call_site_type = TypeManager.CoreLookupType (ec.Compiler,
185 "System.Runtime.CompilerServices", "CallSite", Kind.Class, true);
187 if (TypeManager.generic_call_site_type == null)
188 TypeManager.generic_call_site_type = TypeManager.CoreLookupType (ec.Compiler,
189 "System.Runtime.CompilerServices", "CallSite`1", Kind.Class, true);
191 eclass = ExprClass.Value;
193 if (type == null)
194 type = InternalType.Dynamic;
196 binder_expr = binder.CreateCallSiteBinder (ec, arguments);
197 return this;
200 public override void Emit (EmitContext ec)
202 EmitCall (ec, false);
205 public override void EmitStatement (EmitContext ec)
207 EmitCall (ec, true);
210 void EmitCall (EmitContext ec, bool isStatement)
212 int dyn_args_count = arguments == null ? 0 : arguments.Count;
213 TypeExpr site_type = CreateSiteType (RootContext.ToplevelTypes.Compiler, isStatement, dyn_args_count);
214 FieldExpr site_field_expr = new FieldExpr (CreateSiteField (site_type).FieldBuilder, loc);
216 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
218 Arguments args = new Arguments (1);
219 args.Add (new Argument (binder_expr));
220 StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (site_type, "Create"), args)));
222 BlockContext bc = new BlockContext (ec.MemberContext, null, TypeManager.void_type);
223 if (s.Resolve (bc)) {
224 Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc)), s, loc);
225 init.Emit (ec);
228 args = new Arguments (1 + dyn_args_count);
229 args.Add (new Argument (site_field_expr));
230 if (arguments != null) {
231 foreach (Argument a in arguments) {
232 if (a is NamedArgument) {
233 // Name is not valid in this context
234 args.Add (new Argument (a.Expr, a.ArgType));
235 continue;
238 args.Add (a);
242 ResolveContext rc = new ResolveContext (ec.MemberContext);
243 Expression target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (rc), args, loc).Resolve (rc);
244 if (target != null)
245 target.Emit (ec);
247 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
250 public static MemberAccess GetBinderNamespace (Location loc)
252 return new MemberAccess (new MemberAccess (
253 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "Microsoft", loc), "CSharp", loc), "RuntimeBinder", loc);
256 TypeExpr CreateSiteType (CompilerContext ctx, bool isStatement, int dyn_args_count)
258 int default_args = isStatement ? 1 : 2;
260 bool has_ref_out_argument = false;
261 FullNamedExpression[] targs = new FullNamedExpression[dyn_args_count + default_args];
262 targs [0] = new TypeExpression (TypeManager.call_site_type, loc);
263 for (int i = 0; i < dyn_args_count; ++i) {
264 Type arg_type;
265 Argument a = arguments [i];
266 if (a.Type == TypeManager.null_type)
267 arg_type = TypeManager.object_type;
268 else
269 arg_type = TypeManager.TypeToReflectionType (a.Type);
271 if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref)
272 has_ref_out_argument = true;
274 targs [i + 1] = new TypeExpression (arg_type, loc);
277 TypeExpr del_type = null;
278 if (!has_ref_out_argument) {
279 string d_name = isStatement ? "Action`" : "Func`";
281 Type t = TypeManager.CoreLookupType (ctx, "System", d_name + (dyn_args_count + default_args), Kind.Delegate, false);
282 if (t != null) {
283 if (!isStatement)
284 targs[targs.Length - 1] = new TypeExpression (TypeManager.TypeToReflectionType (type), loc);
286 del_type = new GenericTypeExpr (t, new TypeArguments (targs), loc);
290 // No appropriate predefined delegate found
291 if (del_type == null) {
292 Type rt = isStatement ? TypeManager.void_type : type;
293 Parameter[] p = new Parameter [dyn_args_count + 1];
294 p[0] = new Parameter (targs [0], "p0", Parameter.Modifier.NONE, null, loc);
296 for (int i = 1; i < dyn_args_count + 1; ++i)
297 p[i] = new Parameter (targs[i], "p" + i.ToString ("X"), arguments[i - 1].Modifier, null, loc);
299 TypeContainer parent = CreateSiteContainer ();
300 Delegate d = new Delegate (parent.NamespaceEntry, parent, new TypeExpression (rt, loc),
301 Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED,
302 new MemberName ("Container" + container_counter++.ToString ("X")),
303 new ParametersCompiled (p), null);
305 d.DefineType ();
306 d.Define ();
308 parent.AddDelegate (d);
309 del_type = new TypeExpression (d.TypeBuilder, loc);
312 TypeExpr site_type = new GenericTypeExpr (TypeManager.generic_call_site_type, new TypeArguments (del_type), loc);
313 return site_type;
316 public static void Reset ()
318 global_site_container = null;
319 field_counter = container_counter = 0;
324 // Dynamic member access compound assignment for events
326 class DynamicEventCompoundAssign : DynamicExpressionStatement, IDynamicBinder
328 string name;
329 ExpressionStatement assignment;
330 ExpressionStatement invoke;
332 public DynamicEventCompoundAssign (string name, Arguments args, ExpressionStatement assignment, ExpressionStatement invoke, Location loc)
333 : base (null, args, loc)
335 this.name = name;
336 this.assignment = assignment;
337 this.invoke = invoke;
338 base.binder = this;
340 // Used by += or -= only
341 type = TypeManager.bool_type;
344 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
346 Arguments binder_args = new Arguments (2);
347 MemberAccess binder = GetBinderNamespace (loc);
349 binder_args.Add (new Argument (new StringLiteral (name, loc)));
350 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
352 return new New (new MemberAccess (binder, "CSharpIsEventBinder", loc), binder_args, loc);
355 public override void EmitStatement (EmitContext ec)
357 Statement cond = new If (
358 new Binary (Binary.Operator.Equality, this, new BoolLiteral (true, loc)),
359 new StatementExpression (invoke),
360 new StatementExpression (assignment),
361 loc);
362 cond.Emit (ec);
366 class DynamicConversion : DynamicExpressionStatement, IDynamicBinder
368 bool is_explicit;
370 public DynamicConversion (Type targetType, bool isExplicit, Arguments args, Location loc)
371 : base (null, args, loc)
373 type = targetType;
374 is_explicit = isExplicit;
375 base.binder = this;
378 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
380 Arguments binder_args = new Arguments (2);
381 MemberAccess binder = GetBinderNamespace (loc);
383 binder_args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
384 binder_args.Add (new Argument (new MemberAccess (new MemberAccess (binder, "CSharpConversionKind", loc),
385 is_explicit ? "ExplicitConversion" : "ImplicitConversion", loc)));
386 binder_args.Add (new Argument (new BoolLiteral (ec.HasSet (ResolveContext.Options.CheckedScope), loc)));
388 return new New (new MemberAccess (binder, "CSharpConvertBinder", loc), binder_args, loc);
392 class DynamicIndexBinder : DynamicExpressionStatement, IDynamicBinder, IAssignMethod
394 readonly bool isSet;
396 public DynamicIndexBinder (bool isSet, Arguments args, Location loc)
397 : base (null, args, loc)
399 base.binder = this;
400 this.isSet = isSet;
403 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
405 Arguments binder_args = new Arguments (2);
406 MemberAccess binder = GetBinderNamespace (loc);
408 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
409 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc)));
411 return new New (new MemberAccess (binder, isSet ? "CSharpSetIndexBinder" : "CSharpGetIndexBinder", loc), binder_args, loc);
414 #region IAssignMethod Members
416 public void Emit (EmitContext ec, bool leave_copy)
418 throw new NotImplementedException ();
421 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
423 EmitStatement (ec);
426 #endregion
429 class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder
431 ATypeNameExpression member;
433 public DynamicInvocation (ATypeNameExpression member, Arguments args, Location loc)
434 : base (null, args, loc)
436 base.binder = this;
437 this.member = member;
440 public DynamicInvocation (ATypeNameExpression member, Arguments args, Type type, Location loc)
441 : this (member, args, loc)
443 // When a return type is known not to be dynamic
444 this.type = type;
447 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
449 Arguments binder_args = new Arguments (member != null ? 5 : 3);
450 MemberAccess binder = GetBinderNamespace (loc);
451 bool is_member_access = member is MemberAccess;
453 string call_flags;
454 if (!is_member_access && member is SimpleName) {
455 call_flags = "SimpleNameCall";
456 is_member_access = true;
457 } else {
458 call_flags = "None";
461 binder_args.Add (new Argument (new MemberAccess (new MemberAccess (binder, "CSharpCallFlags", loc), call_flags, loc)));
463 if (is_member_access)
464 binder_args.Add (new Argument (new StringLiteral (member.Name, member.Location)));
466 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
468 if (member != null && member.HasTypeArguments) {
469 TypeArguments ta = member.TypeArguments;
470 if (ta.Resolve (ec)) {
471 ArrayList targs = new ArrayList (ta.Count);
472 foreach (Type t in ta.Arguments)
473 targs.Add (new TypeOf (new TypeExpression (t, loc), loc));
475 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", targs, loc)));
477 } else if (is_member_access) {
478 binder_args.Add (new Argument (new NullLiteral (loc)));
481 Expression real_args;
482 if (args == null) {
483 // Cannot be null because .NET trips over
484 real_args = new ArrayCreation (new MemberAccess (binder, "CSharpArgumentInfo", loc), "[]", new ArrayList (0), loc);
485 } else {
486 real_args = new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc);
489 binder_args.Add (new Argument (real_args));
491 return new New (new MemberAccess (binder,
492 is_member_access ? "CSharpInvokeMemberBinder" : "CSharpInvokeBinder", loc), binder_args, loc);
496 class DynamicMemberBinder : DynamicExpressionStatement, IDynamicBinder, IAssignMethod
498 readonly bool isSet;
499 readonly string name;
501 public DynamicMemberBinder (bool isSet, string name, Arguments args, Location loc)
502 : base (null, args, loc)
504 base.binder = this;
505 this.isSet = isSet;
506 this.name = name;
509 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
511 Arguments binder_args = new Arguments (3);
512 MemberAccess binder = GetBinderNamespace (loc);
514 binder_args.Add (new Argument (new StringLiteral (name, loc)));
515 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
516 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc)));
518 return new New (new MemberAccess (binder, isSet ? "CSharpSetMemberBinder" : "CSharpGetMemberBinder", loc), binder_args, loc);
521 #region IAssignMethod Members
523 public void Emit (EmitContext ec, bool leave_copy)
525 throw new NotImplementedException ();
528 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
530 EmitStatement (ec);
533 #endregion
536 class DynamicUnaryConversion : DynamicExpressionStatement, IDynamicBinder
538 string name;
540 public DynamicUnaryConversion (string name, Arguments args, Location loc)
541 : base (null, args, loc)
543 this.name = name;
544 base.binder = this;
545 if (name == "IsTrue" || name == "IsFalse")
546 type = TypeManager.bool_type;
549 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
551 Arguments binder_args = new Arguments (3);
553 MemberAccess sle = new MemberAccess (new MemberAccess (
554 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc);
556 MemberAccess binder = GetBinderNamespace (loc);
558 binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), name, loc)));
559 binder_args.Add (new Argument (new BoolLiteral (ec.HasSet (ResolveContext.Options.CheckedScope), loc)));
560 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args.CreateDynamicBinderArguments (), loc)));
562 return new New (new MemberAccess (binder, "CSharpUnaryOperationBinder", loc), binder_args, loc);