2 // dynamic.cs: support for dynamic expressions
4 // Authors: Marek Safar (marek.safar@gmail.com)
6 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 // Copyright 2009 Novell, Inc
13 using SLE = System.Linq.Expressions;
22 // A copy of Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpBinderFlags.cs
23 // has to be kept in sync
26 public enum CSharpBinderFlags
30 InvokeSimpleName = 1 << 1,
31 InvokeSpecialName = 1 << 2,
32 BinaryOperationLogical = 1 << 3,
33 ConvertExplicit = 1 << 4,
34 ConvertArrayIndex = 1 << 5,
35 ResultIndexed = 1 << 6,
36 ValueFromCompoundAssignment = 1 << 7,
37 ResultDiscarded = 1 << 8
41 // Type expression with internal dynamic type symbol
43 class DynamicTypeExpr : TypeExpr
45 public DynamicTypeExpr (Location loc)
49 type = InternalType.Dynamic;
50 eclass = ExprClass.Type;
53 public override bool CheckAccessLevel (IMemberContext ds)
58 protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec)
64 #region Dynamic runtime binder expressions
67 // Expression created from runtime dynamic object value by dynamic binder
69 public class RuntimeValueExpression : Expression, IDynamicAssign, IMemoryLocation
72 public class DynamicMetaObject
74 public TypeSpec RuntimeType;
75 public TypeSpec LimitType;
76 public SLE.Expression Expression;
80 readonly DynamicMetaObject obj;
82 public RuntimeValueExpression (DynamicMetaObject obj, TypeSpec type)
86 this.eclass = ExprClass.Variable;
91 public bool IsSuggestionOnly { get; set; }
93 public DynamicMetaObject MetaObject {
99 public void AddressOf (EmitContext ec, AddressOp mode)
101 throw new NotImplementedException ();
104 public override Expression CreateExpressionTree (ResolveContext ec)
106 throw new NotImplementedException ();
109 protected override Expression DoResolve (ResolveContext ec)
114 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
119 public override void Emit (EmitContext ec)
121 throw new NotImplementedException ();
124 #region IAssignMethod Members
126 public void Emit (EmitContext ec, bool leave_copy)
128 throw new NotImplementedException ();
131 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
133 throw new NotImplementedException ();
138 public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source)
140 return obj.Expression;
143 public override SLE.Expression MakeExpression (BuilderContext ctx)
146 if (type.IsStruct && !obj.Expression.Type.IsValueType)
147 return SLE.Expression.Unbox (obj.Expression, type.GetMetaInfo ());
149 if (obj.Expression.NodeType == SLE.ExpressionType.Parameter) {
150 if (((SLE.ParameterExpression) obj.Expression).IsByRef)
151 return obj.Expression;
155 return SLE.Expression.Convert (obj.Expression, type.GetMetaInfo ());
160 // Wraps runtime dynamic expression into expected type. Needed
161 // to satify expected type check by dynamic binder and no conversion
162 // is required (ResultDiscarded).
164 public class DynamicResultCast : ShimExpression
166 public DynamicResultCast (TypeSpec type, Expression expr)
172 protected override Expression DoResolve (ResolveContext ec)
174 expr = expr.Resolve (ec);
175 eclass = ExprClass.Value;
180 public override SLE.Expression MakeExpression (BuilderContext ctx)
182 return SLE.Expression.Block (expr.MakeExpression (ctx), SLE.Expression.Default (type.GetMetaInfo ()));
190 // Creates dynamic binder expression
192 interface IDynamicBinder
194 Expression CreateCallSiteBinder (ResolveContext ec, Arguments args);
198 // Extends standard assignment interface for expressions
199 // supported by dynamic resolver
201 interface IDynamicAssign : IAssignMethod
203 SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source);
207 // Base dynamic expression statement creator
209 class DynamicExpressionStatement : ExpressionStatement
212 // Binder flag dynamic constant, the value is combination of
213 // flags known at resolve stage and flags known only at emit
216 protected class BinderFlags : EnumConstant
218 DynamicExpressionStatement statement;
219 CSharpBinderFlags flags;
221 public BinderFlags (CSharpBinderFlags flags, DynamicExpressionStatement statement)
222 : base (statement.loc)
225 this.statement = statement;
228 protected override Expression DoResolve (ResolveContext ec)
230 Child = new IntConstant ((int) (flags | statement.flags), statement.loc).Resolve (ec);
232 type = TypeManager.binder_flags;
233 eclass = Child.eclass;
238 readonly Arguments arguments;
239 protected IDynamicBinder binder;
240 protected Expression binder_expr;
242 // Used by BinderFlags
243 protected CSharpBinderFlags flags;
245 public DynamicExpressionStatement (IDynamicBinder binder, Arguments args, Location loc)
247 this.binder = binder;
248 this.arguments = args;
252 public Arguments Arguments {
258 FieldSpec CreateSiteField (EmitContext ec, FullNamedExpression type)
260 var site_container = ec.CreateDynamicSite ();
261 return site_container.CreateCallSiteField (type, loc);
264 public override Expression CreateExpressionTree (ResolveContext ec)
266 ec.Report.Error (1963, loc, "An expression tree cannot contain a dynamic operation");
270 protected override Expression DoResolve (ResolveContext ec)
272 if (DoResolveCore (ec))
273 binder_expr = binder.CreateCallSiteBinder (ec, arguments);
278 protected bool DoResolveCore (ResolveContext rc)
280 int errors = rc.Report.Errors;
282 if (TypeManager.binder_type == null) {
283 var t = TypeManager.CoreLookupType (rc.Compiler,
284 "Microsoft.CSharp.RuntimeBinder", "Binder", MemberKind.Class, true);
286 TypeManager.binder_type = new TypeExpression (t, Location.Null);
289 if (TypeManager.call_site_type == null)
290 TypeManager.call_site_type = TypeManager.CoreLookupType (rc.Compiler,
291 "System.Runtime.CompilerServices", "CallSite", MemberKind.Class, true);
293 if (TypeManager.generic_call_site_type == null)
294 TypeManager.generic_call_site_type = TypeManager.CoreLookupType (rc.Compiler,
295 "System.Runtime.CompilerServices", "CallSite", 1, MemberKind.Class, true);
297 if (TypeManager.binder_flags == null) {
298 TypeManager.binder_flags = TypeManager.CoreLookupType (rc.Compiler,
299 "Microsoft.CSharp.RuntimeBinder", "CSharpBinderFlags", MemberKind.Enum, true);
302 eclass = ExprClass.Value;
305 type = InternalType.Dynamic;
307 if (rc.Report.Errors == errors)
310 rc.Report.Error (1969, loc,
311 "Dynamic operation cannot be compiled without `Microsoft.CSharp.dll' assembly reference");
315 public override void Emit (EmitContext ec)
317 EmitCall (ec, binder_expr, arguments, false);
320 public override void EmitStatement (EmitContext ec)
322 EmitCall (ec, binder_expr, arguments, true);
325 protected void EmitCall (EmitContext ec, Expression binder, Arguments arguments, bool isStatement)
327 int dyn_args_count = arguments == null ? 0 : arguments.Count;
328 TypeExpr site_type = CreateSiteType (ec, arguments, dyn_args_count, isStatement);
330 FieldExpr site_field_expr = new FieldExpr (CreateSiteField (ec, site_type), loc);
332 SymbolWriter.OpenCompilerGeneratedBlock (ec);
334 Arguments args = new Arguments (1);
335 args.Add (new Argument (binder));
336 StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (site_type, "Create"), args)));
338 BlockContext bc = new BlockContext (ec.MemberContext, null, TypeManager.void_type);
339 if (s.Resolve (bc)) {
340 Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc), loc), s, loc);
344 args = new Arguments (1 + dyn_args_count);
345 args.Add (new Argument (site_field_expr));
346 if (arguments != null) {
347 foreach (Argument a in arguments) {
348 if (a is NamedArgument) {
349 // Name is not valid in this context
350 args.Add (new Argument (a.Expr, a.ArgType));
358 Expression target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (bc), args, loc).Resolve (bc);
362 SymbolWriter.CloseCompilerGeneratedBlock (ec);
365 public static MemberAccess GetBinderNamespace (Location loc)
367 return new MemberAccess (new MemberAccess (
368 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "Microsoft", loc), "CSharp", loc), "RuntimeBinder", loc);
371 public static MemberAccess GetBinder (string name, Location loc)
373 return new MemberAccess (TypeManager.binder_type, name, loc);
376 TypeExpr CreateSiteType (EmitContext ec, Arguments arguments, int dyn_args_count, bool is_statement)
378 int default_args = is_statement ? 1 : 2;
380 bool has_ref_out_argument = false;
381 var targs = new TypeExpression[dyn_args_count + default_args];
382 targs [0] = new TypeExpression (TypeManager.call_site_type, loc);
383 for (int i = 0; i < dyn_args_count; ++i) {
384 Argument a = arguments [i];
385 if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref)
386 has_ref_out_argument = true;
390 // Convert any internal type like dynamic or null to object
391 if (t.Kind == MemberKind.InternalCompilerType)
392 t = TypeManager.object_type;
394 targs [i + 1] = new TypeExpression (t, loc);
397 TypeExpr del_type = null;
398 if (!has_ref_out_argument) {
399 string d_name = is_statement ? "Action" : "Func";
401 TypeSpec t = TypeManager.CoreLookupType (ec.MemberContext.Compiler, "System", d_name, dyn_args_count + default_args, MemberKind.Delegate, false);
404 targs [targs.Length - 1] = new TypeExpression (type, loc);
406 del_type = new GenericTypeExpr (t, new TypeArguments (targs), loc);
411 // Create custom delegate when no appropriate predefined one is found
413 if (del_type == null) {
414 TypeSpec rt = is_statement ? TypeManager.void_type : type;
415 Parameter[] p = new Parameter [dyn_args_count + 1];
416 p[0] = new Parameter (targs [0], "p0", Parameter.Modifier.NONE, null, loc);
418 var site = ec.CreateDynamicSite ();
419 int index = site.Types == null ? 0 : site.Types.Count;
421 if (site.Mutator != null)
422 rt = site.Mutator.Mutate (rt);
424 for (int i = 1; i < dyn_args_count + 1; ++i) {
426 if (site.Mutator != null)
427 t.Type = site.Mutator.Mutate (t.Type);
429 p[i] = new Parameter (t, "p" + i.ToString ("X"), arguments[i - 1].Modifier, null, loc);
432 Delegate d = new Delegate (site.NamespaceEntry, site, new TypeExpression (rt, loc),
433 Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED,
434 new MemberName ("Container" + index.ToString ("X")),
435 new ParametersCompiled (p), null);
442 var inflated = site.AddDelegate (d);
443 del_type = new TypeExpression (inflated, loc);
446 TypeExpr site_type = new GenericTypeExpr (TypeManager.generic_call_site_type, new TypeArguments (del_type), loc);
452 // Dynamic member access compound assignment for events
454 class DynamicEventCompoundAssign : ExpressionStatement
456 class IsEvent : DynamicExpressionStatement, IDynamicBinder
460 public IsEvent (string name, Arguments args, Location loc)
461 : base (null, args, loc)
467 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
469 type = TypeManager.bool_type;
471 Arguments binder_args = new Arguments (3);
473 binder_args.Add (new Argument (new BinderFlags (0, this)));
474 binder_args.Add (new Argument (new StringLiteral (name, loc)));
475 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
477 return new Invocation (GetBinder ("IsEvent", loc), binder_args);
481 Expression condition;
482 ExpressionStatement invoke, assign;
484 public DynamicEventCompoundAssign (string name, Arguments args, ExpressionStatement assignment, ExpressionStatement invoke, Location loc)
486 condition = new IsEvent (name, args, loc);
487 this.invoke = invoke;
488 this.assign = assignment;
492 public override Expression CreateExpressionTree (ResolveContext ec)
494 return condition.CreateExpressionTree (ec);
497 protected override Expression DoResolve (ResolveContext rc)
499 type = InternalType.Dynamic;
500 eclass = ExprClass.Value;
501 condition = condition.Resolve (rc);
505 public override void Emit (EmitContext ec)
507 var rc = new ResolveContext (ec.MemberContext);
508 var expr = new Conditional (new BooleanExpression (condition), invoke, assign, loc).Resolve (rc);
512 public override void EmitStatement (EmitContext ec)
514 var stmt = new If (condition, new StatementExpression (invoke), new StatementExpression (assign), loc);
519 class DynamicConversion : DynamicExpressionStatement, IDynamicBinder
521 public DynamicConversion (TypeSpec targetType, CSharpBinderFlags flags, Arguments args, Location loc)
522 : base (null, args, loc)
529 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
531 Arguments binder_args = new Arguments (3);
533 flags |= ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0;
535 binder_args.Add (new Argument (new BinderFlags (flags, this)));
536 binder_args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
537 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
538 return new Invocation (GetBinder ("Convert", loc), binder_args);
542 class DynamicConstructorBinder : DynamicExpressionStatement, IDynamicBinder
544 public DynamicConstructorBinder (TypeSpec type, Arguments args, Location loc)
545 : base (null, args, loc)
551 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
553 Arguments binder_args = new Arguments (3);
555 binder_args.Add (new Argument (new BinderFlags (0, this)));
556 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
557 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
559 return new Invocation (GetBinder ("InvokeConstructor", loc), binder_args);
563 class DynamicIndexBinder : DynamicMemberAssignable
565 public DynamicIndexBinder (Arguments args, Location loc)
570 public DynamicIndexBinder (CSharpBinderFlags flags, Arguments args, Location loc)
576 protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet)
578 Arguments binder_args = new Arguments (3);
580 binder_args.Add (new Argument (new BinderFlags (flags, this)));
581 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
582 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
584 isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0;
585 return new Invocation (GetBinder (isSet ? "SetIndex" : "GetIndex", loc), binder_args);
589 class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder
591 ATypeNameExpression member;
593 public DynamicInvocation (ATypeNameExpression member, Arguments args, Location loc)
594 : base (null, args, loc)
597 this.member = member;
601 // When a return type is known not to be dynamic
603 public DynamicInvocation (ATypeNameExpression member, Arguments args, TypeSpec type, Location loc)
604 : this (member, args, loc)
609 public static DynamicInvocation CreateSpecialNameInvoke (ATypeNameExpression member, Arguments args, Location loc)
611 return new DynamicInvocation (member, args, loc) {
612 flags = CSharpBinderFlags.InvokeSpecialName
616 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
618 Arguments binder_args = new Arguments (member != null ? 5 : 3);
619 bool is_member_access = member is MemberAccess;
621 CSharpBinderFlags call_flags;
622 if (!is_member_access && member is SimpleName) {
623 call_flags = CSharpBinderFlags.InvokeSimpleName;
624 is_member_access = true;
629 binder_args.Add (new Argument (new BinderFlags (call_flags, this)));
631 if (is_member_access)
632 binder_args.Add (new Argument (new StringLiteral (member.Name, member.Location)));
634 if (member != null && member.HasTypeArguments) {
635 TypeArguments ta = member.TypeArguments;
636 if (ta.Resolve (ec)) {
637 var targs = new ArrayInitializer (ta.Count, loc);
638 foreach (TypeSpec t in ta.Arguments)
639 targs.Add (new TypeOf (new TypeExpression (t, loc), loc));
641 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (targs, loc)));
643 } else if (is_member_access) {
644 binder_args.Add (new Argument (new NullLiteral (loc)));
647 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
649 Expression real_args;
651 // Cannot be null because .NET trips over
652 real_args = new ArrayCreation (
653 new MemberAccess (GetBinderNamespace (loc), "CSharpArgumentInfo", loc),
654 new ArrayInitializer (0, loc), loc);
656 real_args = new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc);
659 binder_args.Add (new Argument (real_args));
661 return new Invocation (GetBinder (is_member_access ? "InvokeMember" : "Invoke", loc), binder_args);
664 public override void EmitStatement (EmitContext ec)
666 flags |= CSharpBinderFlags.ResultDiscarded;
667 base.EmitStatement (ec);
671 class DynamicMemberBinder : DynamicMemberAssignable
673 readonly string name;
675 public DynamicMemberBinder (string name, Arguments args, Location loc)
681 public DynamicMemberBinder (string name, CSharpBinderFlags flags, Arguments args, Location loc)
682 : this (name, args, loc)
687 protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet)
689 Arguments binder_args = new Arguments (4);
691 binder_args.Add (new Argument (new BinderFlags (flags, this)));
692 binder_args.Add (new Argument (new StringLiteral (name, loc)));
693 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
694 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
696 isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0;
697 return new Invocation (GetBinder (isSet ? "SetMember" : "GetMember", loc), binder_args);
702 // Any member binder which can be source and target of assignment
704 abstract class DynamicMemberAssignable : DynamicExpressionStatement, IDynamicBinder, IAssignMethod
707 Arguments setter_args;
709 protected DynamicMemberAssignable (Arguments args, Location loc)
710 : base (null, args, loc)
715 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
718 // DoResolve always uses getter
720 return CreateCallSiteBinder (ec, args, false);
723 protected abstract Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet);
725 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
727 if (right_side == EmptyExpression.OutAccess.Instance) {
728 right_side.DoResolveLValue (rc, this);
732 if (DoResolveCore (rc)) {
733 setter_args = new Arguments (Arguments.Count + 1);
734 setter_args.AddRange (Arguments);
735 setter_args.Add (new Argument (right_side));
736 setter = CreateCallSiteBinder (rc, setter_args, true);
739 eclass = ExprClass.Variable;
743 public override void Emit (EmitContext ec)
745 // It's null for ResolveLValue used without assignment
746 if (binder_expr == null)
747 EmitCall (ec, setter, Arguments, false);
752 public override void EmitStatement (EmitContext ec)
754 // It's null for ResolveLValue used without assignment
755 if (binder_expr == null)
756 EmitCall (ec, setter, Arguments, true);
758 base.EmitStatement (ec);
761 #region IAssignMethod Members
763 public void Emit (EmitContext ec, bool leave_copy)
765 throw new NotImplementedException ();
768 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
770 EmitCall (ec, setter, setter_args, !leave_copy);
776 class DynamicUnaryConversion : DynamicExpressionStatement, IDynamicBinder
780 public DynamicUnaryConversion (string name, Arguments args, Location loc)
781 : base (null, args, loc)
785 if (name == "IsTrue" || name == "IsFalse")
786 type = TypeManager.bool_type;
789 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
791 Arguments binder_args = new Arguments (4);
793 MemberAccess sle = new MemberAccess (new MemberAccess (
794 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc);
796 var flags = ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0;
798 binder_args.Add (new Argument (new BinderFlags (flags, this)));
799 binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), name, loc)));
800 binder_args.Add (new Argument (new TypeOf (new TypeExpression (ec.CurrentType, loc), loc)));
801 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
803 return new Invocation (GetBinder ("UnaryOperation", loc), binder_args);
807 public class DynamicSiteClass : HoistedStoreyClass
810 // Holds the type to access the site. It gets inflated
811 // by MVARs for generic call sites
813 TypeSpec instance_type;
815 public DynamicSiteClass (TypeContainer parent, MemberBase host, TypeParameter[] tparams)
816 : base (parent, MakeMemberName (host, "DynamicSite", parent.DynamicSitesCounter, tparams, Location.Null), tparams, Modifiers.STATIC)
818 if (tparams != null) {
819 mutator = new TypeParameterMutator (tparams, CurrentTypeParameters);
822 parent.DynamicSitesCounter++;
825 public override TypeSpec AddDelegate (Delegate d)
829 base.AddDelegate (d);
831 // Inflated type instance has to be updated manually
832 if (instance_type is InflatedTypeSpec) {
833 var inflator = new TypeParameterInflator (instance_type, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
834 inflated = (TypeSpec) d.CurrentType.InflateMember (inflator);
835 instance_type.MemberCache.AddMember (inflated);
837 //inflator = new TypeParameterInflator (d.Parent.CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
838 //d.Parent.CurrentType.MemberCache.AddMember (d.CurrentType.InflateMember (inflator));
840 inflated = d.CurrentType;
846 public FieldSpec CreateCallSiteField (FullNamedExpression type, Location loc)
848 int index = fields == null ? 0 : fields.Count;
849 Field f = new HoistedField (this, type, Modifiers.PUBLIC | Modifiers.STATIC, "Site" + index.ToString ("X"), null, loc);
855 if (mutator != null) {
857 // Inflate the field, no need to keep it in MemberCache as it's accessed only once
859 var inflator = new TypeParameterInflator (instance_type, spec.MemberDefinition.TypeParameters, instance_type.TypeArguments);
860 fs = (FieldSpec) fs.InflateMember (inflator);
866 protected override bool DoResolveTypeParameters ()
868 instance_type = spec;
870 instance_type = instance_type.MakeGenericType (mutator.MethodTypeParameters.Select (l => l.Type).ToArray ());