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
12 using System
.Reflection
.Emit
;
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 Type RuntimeType
;
75 public Type LimitType
;
76 public SLE
.Expression Expression
;
80 readonly DynamicMetaObject obj
;
82 public RuntimeValueExpression (DynamicMetaObject obj
, bool isCompileTimeType
)
85 this.type
= isCompileTimeType
? obj
.LimitType
: obj
.RuntimeType
;
86 this.type
= obj
.LimitType
;
87 this.eclass
= ExprClass
.Variable
;
90 public void AddressOf (EmitContext ec
, AddressOp mode
)
92 throw new NotImplementedException ();
95 public override Expression
CreateExpressionTree (ResolveContext ec
)
97 throw new NotImplementedException ();
100 protected override Expression
DoResolve (ResolveContext ec
)
105 public override Expression
DoResolveLValue (ResolveContext ec
, Expression right_side
)
110 public override void Emit (EmitContext ec
)
112 throw new NotImplementedException ();
115 #region IAssignMethod Members
117 public void Emit (EmitContext ec
, bool leave_copy
)
119 throw new NotImplementedException ();
122 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
124 throw new NotImplementedException ();
129 public SLE
.Expression
MakeAssignExpression (BuilderContext ctx
)
131 return obj
.Expression
;
134 public override SLE
.Expression
MakeExpression (BuilderContext ctx
)
136 return SLE
.Expression
.Convert (obj
.Expression
, type
);
139 public DynamicMetaObject MetaObject
{
145 // Wraps runtime dynamic expression into expected type. Needed
146 // to satify expected type check by dynamic binder and no conversion
147 // is required (ResultDiscarded).
149 public class DynamicResultCast
: ShimExpression
151 public DynamicResultCast (Type type
, Expression expr
)
157 protected override Expression
DoResolve (ResolveContext ec
)
159 expr
= expr
.Resolve (ec
);
160 eclass
= ExprClass
.Value
;
165 public override SLE
.Expression
MakeExpression (BuilderContext ctx
)
167 return SLE
.Expression
.Block (expr
.MakeExpression (ctx
), SLE
.Expression
.Default (type
));
175 // Creates dynamic binder expression
177 interface IDynamicBinder
179 Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
);
183 // Extends standard assignment interface for expressions
184 // supported by dynamic resolver
186 interface IDynamicAssign
: IAssignMethod
189 SLE
.Expression
MakeAssignExpression (BuilderContext ctx
);
194 // Base dynamic expression statement creator
196 class DynamicExpressionStatement
: ExpressionStatement
198 class StaticDataClass
: CompilerGeneratedClass
200 public StaticDataClass ()
201 : base (new RootDeclSpace (new NamespaceEntry (null, null, null)),
202 new MemberName (CompilerGeneratedClass
.MakeName (null, "c", "DynamicSites", 0)),
203 Modifiers
.INTERNAL
| Modifiers
.STATIC
)
209 // Binder flag dynamic constant, the value is combination of
210 // flags known at resolve stage and flags known only at emit
213 protected class BinderFlags
: EnumConstant
215 DynamicExpressionStatement statement
;
216 CSharpBinderFlags flags
;
218 public BinderFlags (CSharpBinderFlags flags
, DynamicExpressionStatement statement
)
219 : base (statement
.loc
)
222 this.statement
= statement
;
225 protected override Expression
DoResolve (ResolveContext ec
)
227 Child
= new IntConstant ((int) (flags
| statement
.flags
), statement
.loc
).Resolve (ec
);
229 type
= TypeManager
.binder_flags
;
230 eclass
= Child
.eclass
;
235 static StaticDataClass global_site_container
;
236 static int field_counter
;
237 static int container_counter
;
239 readonly Arguments arguments
;
240 protected IDynamicBinder binder
;
241 protected Expression binder_expr
;
243 // Used by BinderFlags
244 protected CSharpBinderFlags flags
;
246 public DynamicExpressionStatement (IDynamicBinder binder
, Arguments args
, Location loc
)
248 this.binder
= binder
;
249 this.arguments
= args
;
253 public Arguments Arguments
{
259 static TypeContainer
CreateSiteContainer ()
261 if (global_site_container
== null) {
262 global_site_container
= new StaticDataClass ();
263 RootContext
.ToplevelTypes
.AddCompilerGeneratedClass (global_site_container
);
264 global_site_container
.DefineType ();
265 global_site_container
.Define ();
268 return global_site_container
;
271 static Field
CreateSiteField (FullNamedExpression type
)
273 TypeContainer site_container
= CreateSiteContainer ();
274 Field f
= new Field (site_container
, type
, Modifiers
.PUBLIC
| Modifiers
.STATIC
,
275 new MemberName ("Site" + field_counter
++), null);
278 site_container
.AddField (f
);
282 public override Expression
CreateExpressionTree (ResolveContext ec
)
284 ec
.Report
.Error (1963, loc
, "An expression tree cannot contain a dynamic operation");
288 protected override Expression
DoResolve (ResolveContext ec
)
290 if (DoResolveCore (ec
))
291 binder_expr
= binder
.CreateCallSiteBinder (ec
, arguments
);
296 protected bool DoResolveCore (ResolveContext rc
)
298 int errors
= rc
.Report
.Errors
;
300 if (TypeManager
.binder_type
== null) {
301 var t
= TypeManager
.CoreLookupType (rc
.Compiler
,
302 "Microsoft.CSharp.RuntimeBinder", "Binder", MemberKind
.Class
, true);
304 TypeManager
.binder_type
= new TypeExpression (t
, Location
.Null
);
307 if (TypeManager
.call_site_type
== null)
308 TypeManager
.call_site_type
= TypeManager
.CoreLookupType (rc
.Compiler
,
309 "System.Runtime.CompilerServices", "CallSite", MemberKind
.Class
, true);
311 if (TypeManager
.generic_call_site_type
== null)
312 TypeManager
.generic_call_site_type
= TypeManager
.CoreLookupType (rc
.Compiler
,
313 "System.Runtime.CompilerServices", "CallSite`1", MemberKind
.Class
, true);
315 if (TypeManager
.binder_flags
== null) {
316 TypeManager
.binder_flags
= TypeManager
.CoreLookupType (rc
.Compiler
,
317 "Microsoft.CSharp.RuntimeBinder", "CSharpBinderFlags", MemberKind
.Enum
, true);
320 eclass
= ExprClass
.Value
;
323 type
= InternalType
.Dynamic
;
325 if (rc
.Report
.Errors
== errors
)
328 rc
.Report
.Error (1969, loc
,
329 "Dynamic operation cannot be compiled without `Microsoft.CSharp.dll' assembly reference");
333 public override void Emit (EmitContext ec
)
335 EmitCall (ec
, binder_expr
, arguments
, false);
338 public override void EmitStatement (EmitContext ec
)
340 EmitCall (ec
, binder_expr
, arguments
, true);
343 protected void EmitCall (EmitContext ec
, Expression binder
, Arguments arguments
, bool isStatement
)
345 int dyn_args_count
= arguments
== null ? 0 : arguments
.Count
;
346 TypeExpr site_type
= CreateSiteType (RootContext
.ToplevelTypes
.Compiler
, arguments
, dyn_args_count
, isStatement
);
347 FieldExpr site_field_expr
= new FieldExpr (CreateSiteField (site_type
), loc
);
349 SymbolWriter
.OpenCompilerGeneratedBlock (ec
.ig
);
351 Arguments args
= new Arguments (1);
352 args
.Add (new Argument (binder
));
353 StatementExpression s
= new StatementExpression (new SimpleAssign (site_field_expr
, new Invocation (new MemberAccess (site_type
, "Create"), args
)));
355 BlockContext bc
= new BlockContext (ec
.MemberContext
, null, TypeManager
.void_type
);
356 if (s
.Resolve (bc
)) {
357 Statement init
= new If (new Binary (Binary
.Operator
.Equality
, site_field_expr
, new NullLiteral (loc
), loc
), s
, loc
);
361 args
= new Arguments (1 + dyn_args_count
);
362 args
.Add (new Argument (site_field_expr
));
363 if (arguments
!= null) {
364 foreach (Argument a
in arguments
) {
365 if (a
is NamedArgument
) {
366 // Name is not valid in this context
367 args
.Add (new Argument (a
.Expr
, a
.ArgType
));
375 Expression target
= new DelegateInvocation (new MemberAccess (site_field_expr
, "Target", loc
).Resolve (bc
), args
, loc
).Resolve (bc
);
379 SymbolWriter
.CloseCompilerGeneratedBlock (ec
.ig
);
382 public static MemberAccess
GetBinderNamespace (Location loc
)
384 return new MemberAccess (new MemberAccess (
385 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "Microsoft", loc
), "CSharp", loc
), "RuntimeBinder", loc
);
388 public static MemberAccess
GetBinder (string name
, Location loc
)
390 return new MemberAccess (TypeManager
.binder_type
, name
, loc
);
393 TypeExpr
CreateSiteType (CompilerContext ctx
, Arguments arguments
, int dyn_args_count
, bool is_statement
)
395 int default_args
= is_statement
? 1 : 2;
397 bool has_ref_out_argument
= false;
398 FullNamedExpression
[] targs
= new FullNamedExpression
[dyn_args_count
+ default_args
];
399 targs
[0] = new TypeExpression (TypeManager
.call_site_type
, loc
);
400 for (int i
= 0; i
< dyn_args_count
; ++i
) {
402 Argument a
= arguments
[i
];
403 if (a
.Type
== TypeManager
.null_type
)
404 arg_type
= TypeManager
.object_type
;
408 if (a
.ArgType
== Argument
.AType
.Out
|| a
.ArgType
== Argument
.AType
.Ref
)
409 has_ref_out_argument
= true;
411 targs
[i
+ 1] = new TypeExpression (arg_type
, loc
);
414 TypeExpr del_type
= null;
415 if (!has_ref_out_argument
) {
416 string d_name
= is_statement
? "Action`" : "Func`";
418 Type t
= TypeManager
.CoreLookupType (ctx
, "System", d_name
+ (dyn_args_count
+ default_args
), MemberKind
.Delegate
, false);
421 targs
[targs
.Length
- 1] = new TypeExpression (type
, loc
);
423 del_type
= new GenericTypeExpr (t
, new TypeArguments (targs
), loc
);
428 // Create custom delegate when no appropriate predefined one is found
430 if (del_type
== null) {
431 Type rt
= is_statement
? TypeManager
.void_type
: type
;
432 Parameter
[] p
= new Parameter
[dyn_args_count
+ 1];
433 p
[0] = new Parameter (targs
[0], "p0", Parameter
.Modifier
.NONE
, null, loc
);
435 for (int i
= 1; i
< dyn_args_count
+ 1; ++i
)
436 p
[i
] = new Parameter (targs
[i
], "p" + i
.ToString ("X"), arguments
[i
- 1].Modifier
, null, loc
);
438 TypeContainer parent
= CreateSiteContainer ();
439 Delegate d
= new Delegate (parent
.NamespaceEntry
, parent
, new TypeExpression (rt
, loc
),
440 Modifiers
.INTERNAL
| Modifiers
.COMPILER_GENERATED
,
441 new MemberName ("Container" + container_counter
++.ToString ("X")),
442 new ParametersCompiled (ctx
, p
), null);
448 parent
.AddDelegate (d
);
449 del_type
= new TypeExpression (d
.TypeBuilder
, loc
);
452 TypeExpr site_type
= new GenericTypeExpr (TypeManager
.generic_call_site_type
, new TypeArguments (del_type
), loc
);
456 public static void Reset ()
458 global_site_container
= null;
459 field_counter
= container_counter
= 0;
464 // Dynamic member access compound assignment for events
466 class DynamicEventCompoundAssign
: DynamicExpressionStatement
, IDynamicBinder
471 public DynamicEventCompoundAssign (string name
, Arguments args
, ExpressionStatement assignment
, ExpressionStatement invoke
, Location loc
)
472 : base (null, args
, loc
)
477 // Used by += or -= only
478 type
= TypeManager
.bool_type
;
481 new Binary (Binary
.Operator
.Equality
, this, new BoolLiteral (true, loc
), loc
),
482 new StatementExpression (invoke
), new StatementExpression (assignment
),
486 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
488 Arguments binder_args
= new Arguments (3);
490 binder_args
.Add (new Argument (new BinderFlags (0, this)));
491 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
492 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
494 return new Invocation (GetBinder ("IsEvent", loc
), binder_args
);
497 public override void EmitStatement (EmitContext ec
)
503 class DynamicConversion
: DynamicExpressionStatement
, IDynamicBinder
505 public DynamicConversion (Type targetType
, CSharpBinderFlags flags
, Arguments args
, Location loc
)
506 : base (null, args
, loc
)
513 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
515 Arguments binder_args
= new Arguments (3);
517 flags
|= ec
.HasSet (ResolveContext
.Options
.CheckedScope
) ? CSharpBinderFlags
.CheckedContext
: 0;
519 binder_args
.Add (new Argument (new BinderFlags (flags
, this)));
520 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
521 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
522 return new Invocation (GetBinder ("Convert", loc
), binder_args
);
526 class DynamicConstructorBinder
: DynamicExpressionStatement
, IDynamicBinder
528 public DynamicConstructorBinder (Type type
, Arguments args
, Location loc
)
529 : base (null, args
, loc
)
535 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
537 Arguments binder_args
= new Arguments (3);
539 binder_args
.Add (new Argument (new BinderFlags (0, this)));
540 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
541 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (ec
), loc
)));
543 return new Invocation (GetBinder ("InvokeConstructor", loc
), binder_args
);
547 class DynamicIndexBinder
: DynamicMemberAssignable
549 public DynamicIndexBinder (Arguments args
, Location loc
)
554 protected override Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
, bool isSet
)
556 Arguments binder_args
= new Arguments (3);
558 binder_args
.Add (new Argument (new BinderFlags (0, this)));
559 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
560 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (ec
), loc
)));
562 return new Invocation (GetBinder (isSet
? "SetIndex" : "GetIndex", loc
), binder_args
);
566 class DynamicInvocation
: DynamicExpressionStatement
, IDynamicBinder
568 ATypeNameExpression member
;
570 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Location loc
)
571 : base (null, args
, loc
)
574 this.member
= member
;
577 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Type type
, Location loc
)
578 : this (member
, args
, loc
)
580 // When a return type is known not to be dynamic
584 public static DynamicInvocation
CreateSpecialNameInvoke (ATypeNameExpression member
, Arguments args
, Location loc
)
586 return new DynamicInvocation (member
, args
, loc
) {
587 flags
= CSharpBinderFlags
.InvokeSpecialName
591 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
593 Arguments binder_args
= new Arguments (member
!= null ? 5 : 3);
594 bool is_member_access
= member
is MemberAccess
;
596 CSharpBinderFlags call_flags
;
597 if (!is_member_access
&& member
is SimpleName
) {
598 call_flags
= CSharpBinderFlags
.InvokeSimpleName
;
599 is_member_access
= true;
604 binder_args
.Add (new Argument (new BinderFlags (call_flags
, this)));
606 if (is_member_access
)
607 binder_args
.Add (new Argument (new StringLiteral (member
.Name
, member
.Location
)));
609 if (member
!= null && member
.HasTypeArguments
) {
610 TypeArguments ta
= member
.TypeArguments
;
611 if (ta
.Resolve (ec
)) {
612 var targs
= new ArrayInitializer (ta
.Count
, loc
);
613 foreach (Type t
in ta
.Arguments
)
614 targs
.Add (new TypeOf (new TypeExpression (t
, loc
), loc
));
616 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", targs
, loc
)));
618 } else if (is_member_access
) {
619 binder_args
.Add (new Argument (new NullLiteral (loc
)));
622 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
624 Expression real_args
;
626 // Cannot be null because .NET trips over
627 real_args
= new ArrayCreation (
628 new MemberAccess (GetBinderNamespace (loc
), "CSharpArgumentInfo", loc
), "[]",
629 new ArrayInitializer (0, loc
), loc
);
631 real_args
= new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (ec
), loc
);
634 binder_args
.Add (new Argument (real_args
));
636 return new Invocation (GetBinder (is_member_access
? "InvokeMember" : "Invoke", loc
), binder_args
);
639 public override void EmitStatement (EmitContext ec
)
641 flags
|= CSharpBinderFlags
.ResultDiscarded
;
642 base.EmitStatement (ec
);
646 class DynamicMemberBinder
: DynamicMemberAssignable
648 readonly string name
;
650 public DynamicMemberBinder (string name
, Arguments args
, Location loc
)
656 protected override Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
, bool isSet
)
658 Arguments binder_args
= new Arguments (4);
660 binder_args
.Add (new Argument (new BinderFlags (0, this)));
661 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
662 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
663 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (ec
), loc
)));
665 return new Invocation (GetBinder (isSet
? "SetMember" : "GetMember", loc
), binder_args
);
670 // Any member binder which can be source and target of assignment
672 abstract class DynamicMemberAssignable
: DynamicExpressionStatement
, IDynamicBinder
, IAssignMethod
675 Arguments setter_args
;
677 protected DynamicMemberAssignable (Arguments args
, Location loc
)
678 : base (null, args
, loc
)
683 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
686 // DoResolve always uses getter
688 return CreateCallSiteBinder (ec
, args
, false);
691 protected abstract Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
, bool isSet
);
693 public override Expression
DoResolveLValue (ResolveContext rc
, Expression right_side
)
695 if (right_side
== EmptyExpression
.OutAccess
.Instance
) {
696 right_side
.DoResolveLValue (rc
, this);
700 if (DoResolveCore (rc
)) {
701 setter_args
= new Arguments (Arguments
.Count
+ 1);
702 setter_args
.AddRange (Arguments
);
703 setter_args
.Add (new Argument (right_side
));
704 setter
= CreateCallSiteBinder (rc
, setter_args
, true);
707 eclass
= ExprClass
.Variable
;
711 public override void Emit (EmitContext ec
)
713 // It's null for ResolveLValue used without assignment
714 if (binder_expr
== null)
715 EmitCall (ec
, setter
, Arguments
, false);
720 public override void EmitStatement (EmitContext ec
)
722 // It's null for ResolveLValue used without assignment
723 if (binder_expr
== null)
724 EmitCall (ec
, setter
, Arguments
, true);
726 base.EmitStatement (ec
);
729 #region IAssignMethod Members
731 public void Emit (EmitContext ec
, bool leave_copy
)
733 throw new NotImplementedException ();
736 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
738 EmitCall (ec
, setter
, setter_args
, !leave_copy
);
744 class DynamicUnaryConversion
: DynamicExpressionStatement
, IDynamicBinder
748 public DynamicUnaryConversion (string name
, Arguments args
, Location loc
)
749 : base (null, args
, loc
)
753 if (name
== "IsTrue" || name
== "IsFalse")
754 type
= TypeManager
.bool_type
;
757 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
759 Arguments binder_args
= new Arguments (4);
761 MemberAccess sle
= new MemberAccess (new MemberAccess (
762 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Linq", loc
), "Expressions", loc
);
764 var flags
= ec
.HasSet (ResolveContext
.Options
.CheckedScope
) ? CSharpBinderFlags
.CheckedContext
: 0;
766 binder_args
.Add (new Argument (new BinderFlags (flags
, this)));
767 binder_args
.Add (new Argument (new MemberAccess (new MemberAccess (sle
, "ExpressionType", loc
), name
, loc
)));
768 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
769 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (ec
), loc
)));
771 return new Invocation (GetBinder ("UnaryOperation", loc
), binder_args
);