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
.Collections
;
13 using System
.Reflection
.Emit
;
17 using SLE
= System
.Linq
.Expressions
;
23 // A copy of Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpBinderFlags.cs
24 // has to be kept in sync
27 public enum CSharpBinderFlags
31 InvokeSimpleName
= 1 << 1,
32 InvokeSpecialName
= 1 << 2,
33 BinaryOperationLogical
= 1 << 3,
34 ConvertExplicit
= 1 << 4,
35 ConvertArrayIndex
= 1 << 5,
36 ResultIndexed
= 1 << 6,
37 ValueFromCompoundAssignment
= 1 << 7,
38 ResultDiscarded
= 1 << 8
42 // Type expression with internal dynamic type symbol
44 class DynamicTypeExpr
: TypeExpr
46 public DynamicTypeExpr (Location loc
)
50 type
= InternalType
.Dynamic
;
51 eclass
= ExprClass
.Type
;
54 public override bool CheckAccessLevel (IMemberContext ds
)
59 protected override TypeExpr
DoResolveAsTypeStep (IMemberContext ec
)
66 // Expression created from runtime dynamic object value
68 public class RuntimeValueExpression
: Expression
, IDynamicAssign
71 public class DynamicMetaObject { public Type RuntimeType; }
74 readonly DynamicMetaObject obj
;
76 public RuntimeValueExpression (DynamicMetaObject obj
)
79 this.type
= obj
.RuntimeType
;
80 this.eclass
= ExprClass
.Variable
;
83 public override Expression
CreateExpressionTree (ResolveContext ec
)
85 throw new NotImplementedException ();
88 public override Expression
DoResolve (ResolveContext ec
)
93 public override Expression
DoResolveLValue (ResolveContext ec
, Expression right_side
)
98 public override void Emit (EmitContext ec
)
100 throw new NotImplementedException ();
103 #region IAssignMethod Members
105 public void Emit (EmitContext ec
, bool leave_copy
)
107 throw new NotImplementedException ();
110 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
112 throw new NotImplementedException ();
118 public SLE
.Expression
MakeAssignExpression (BuilderContext ctx
)
120 return obj
.Expression
;
123 public override SLE
.Expression
MakeExpression (BuilderContext ctx
)
125 return SLE
.Expression
.Convert (obj
.Expression
, type
);
129 public DynamicMetaObject MetaObject
{
135 // Creates dynamic binder expression
137 interface IDynamicBinder
139 Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
);
143 // Extends standard assignment interface for expressions
144 // supported by dynamic resolver
146 interface IDynamicAssign
: IAssignMethod
149 SLE
.Expression
MakeAssignExpression (BuilderContext ctx
);
154 // Base dynamic expression statement creator
156 class DynamicExpressionStatement
: ExpressionStatement
158 class StaticDataClass
: CompilerGeneratedClass
160 public StaticDataClass ()
161 : base (new RootDeclSpace (new NamespaceEntry (null, null, null)),
162 new MemberName (CompilerGeneratedClass
.MakeName (null, "c", "DynamicSites", 0)),
163 Modifiers
.INTERNAL
| Modifiers
.STATIC
)
165 ModFlags
&= ~Modifiers
.SEALED
;
170 // Binder flag dynamic constant, the value is combination of
171 // flags known at resolve stage and flags known only at emit
174 protected class BinderFlags
: EnumConstant
176 DynamicExpressionStatement statement
;
177 CSharpBinderFlags flags
;
179 public BinderFlags (CSharpBinderFlags flags
, DynamicExpressionStatement statement
)
183 this.statement
= statement
;
186 public override Expression
DoResolve (ResolveContext ec
)
188 Child
= new IntConstant ((int) (flags
| statement
.flags
), statement
.loc
);
190 type
= TypeManager
.binder_flags
;
191 eclass
= Child
.eclass
;
196 static StaticDataClass global_site_container
;
197 static int field_counter
;
198 static int container_counter
;
200 readonly Arguments arguments
;
201 protected IDynamicBinder binder
;
202 Expression binder_expr
;
204 // Used by BinderFlags
205 protected CSharpBinderFlags flags
;
207 public DynamicExpressionStatement (IDynamicBinder binder
, Arguments args
, Location loc
)
209 this.binder
= binder
;
210 this.arguments
= args
;
214 public Arguments Arguments
{
220 static TypeContainer
CreateSiteContainer ()
222 if (global_site_container
== null) {
223 global_site_container
= new StaticDataClass ();
224 RootContext
.ToplevelTypes
.AddCompilerGeneratedClass (global_site_container
);
225 global_site_container
.DefineType ();
226 global_site_container
.Define ();
227 // global_site_container.EmitType ();
229 RootContext
.RegisterCompilerGeneratedType (global_site_container
.TypeBuilder
);
232 return global_site_container
;
235 static Field
CreateSiteField (FullNamedExpression type
)
237 TypeContainer site_container
= CreateSiteContainer ();
238 Field f
= new Field (site_container
, type
, Modifiers
.PUBLIC
| Modifiers
.STATIC
,
239 new MemberName ("Site" + field_counter
++), null);
242 site_container
.AddField (f
);
246 public override Expression
CreateExpressionTree (ResolveContext ec
)
248 throw new NotImplementedException ();
251 public override Expression
DoResolve (ResolveContext ec
)
253 if (eclass
!= ExprClass
.Invalid
)
256 if (DoResolveCore (ec
))
257 binder_expr
= binder
.CreateCallSiteBinder (ec
, arguments
);
262 protected bool DoResolveCore (ResolveContext rc
)
264 int errors
= rc
.Report
.Errors
;
266 if (TypeManager
.call_site_type
== null)
267 TypeManager
.call_site_type
= TypeManager
.CoreLookupType (rc
.Compiler
,
268 "System.Runtime.CompilerServices", "CallSite", Kind
.Class
, true);
270 if (TypeManager
.generic_call_site_type
== null)
271 TypeManager
.generic_call_site_type
= TypeManager
.CoreLookupType (rc
.Compiler
,
272 "System.Runtime.CompilerServices", "CallSite`1", Kind
.Class
, true);
274 if (TypeManager
.binder_type
== null) {
275 var t
= TypeManager
.CoreLookupType (rc
.Compiler
,
276 "Microsoft.CSharp.RuntimeBinder", "Binder", Kind
.Class
, true);
278 TypeManager
.binder_type
= new TypeExpression (t
, Location
.Null
);
281 if (TypeManager
.binder_flags
== null) {
282 TypeManager
.binder_flags
= TypeManager
.CoreLookupType (rc
.Compiler
,
283 "Microsoft.CSharp.RuntimeBinder", "CSharpBinderFlags", Kind
.Enum
, true);
286 eclass
= ExprClass
.Value
;
289 type
= InternalType
.Dynamic
;
291 return rc
.Report
.Errors
== errors
;
294 public override void Emit (EmitContext ec
)
296 EmitCall (ec
, binder_expr
, arguments
, false);
299 public override void EmitStatement (EmitContext ec
)
301 EmitCall (ec
, binder_expr
, arguments
, true);
304 protected void EmitCall (EmitContext ec
, Expression binder
, Arguments arguments
, bool isStatement
)
306 int dyn_args_count
= arguments
== null ? 0 : arguments
.Count
;
307 TypeExpr site_type
= CreateSiteType (RootContext
.ToplevelTypes
.Compiler
, arguments
, dyn_args_count
, isStatement
);
308 FieldExpr site_field_expr
= new FieldExpr (CreateSiteField (site_type
).FieldBuilder
, loc
);
310 SymbolWriter
.OpenCompilerGeneratedBlock (ec
.ig
);
312 Arguments args
= new Arguments (1);
313 args
.Add (new Argument (binder
));
314 StatementExpression s
= new StatementExpression (new SimpleAssign (site_field_expr
, new Invocation (new MemberAccess (site_type
, "Create"), args
)));
316 BlockContext bc
= new BlockContext (ec
.MemberContext
, null, TypeManager
.void_type
);
317 if (s
.Resolve (bc
)) {
318 Statement init
= new If (new Binary (Binary
.Operator
.Equality
, site_field_expr
, new NullLiteral (loc
)), s
, loc
);
322 args
= new Arguments (1 + dyn_args_count
);
323 args
.Add (new Argument (site_field_expr
));
324 if (arguments
!= null) {
325 foreach (Argument a
in arguments
) {
326 if (a
is NamedArgument
) {
327 // Name is not valid in this context
328 args
.Add (new Argument (a
.Expr
, a
.ArgType
));
336 Expression target
= new DelegateInvocation (new MemberAccess (site_field_expr
, "Target", loc
).Resolve (bc
), args
, loc
).Resolve (bc
);
340 SymbolWriter
.CloseCompilerGeneratedBlock (ec
.ig
);
343 public static MemberAccess
GetBinderNamespace (Location loc
)
345 return new MemberAccess (new MemberAccess (
346 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "Microsoft", loc
), "CSharp", loc
), "RuntimeBinder", loc
);
349 public static MemberAccess
GetBinder (string name
, Location loc
)
351 return new MemberAccess (TypeManager
.binder_type
, name
, loc
);
354 TypeExpr
CreateSiteType (CompilerContext ctx
, Arguments arguments
, int dyn_args_count
, bool is_statement
)
356 int default_args
= is_statement
? 1 : 2;
358 bool has_ref_out_argument
= false;
359 FullNamedExpression
[] targs
= new FullNamedExpression
[dyn_args_count
+ default_args
];
360 targs
[0] = new TypeExpression (TypeManager
.call_site_type
, loc
);
361 for (int i
= 0; i
< dyn_args_count
; ++i
) {
363 Argument a
= arguments
[i
];
364 if (a
.Type
== TypeManager
.null_type
)
365 arg_type
= TypeManager
.object_type
;
367 arg_type
= TypeManager
.TypeToReflectionType (a
.Type
);
369 if (a
.ArgType
== Argument
.AType
.Out
|| a
.ArgType
== Argument
.AType
.Ref
)
370 has_ref_out_argument
= true;
372 targs
[i
+ 1] = new TypeExpression (arg_type
, loc
);
375 TypeExpr del_type
= null;
376 if (!has_ref_out_argument
) {
377 string d_name
= is_statement
? "Action`" : "Func`";
379 Type t
= TypeManager
.CoreLookupType (ctx
, "System", d_name
+ (dyn_args_count
+ default_args
), Kind
.Delegate
, false);
382 targs
[targs
.Length
- 1] = new TypeExpression (TypeManager
.TypeToReflectionType (type
), loc
);
384 del_type
= new GenericTypeExpr (t
, new TypeArguments (targs
), loc
);
389 // Create custom delegate when no appropriate predefined one is found
391 if (del_type
== null) {
392 Type rt
= is_statement
? TypeManager
.void_type
: type
;
393 Parameter
[] p
= new Parameter
[dyn_args_count
+ 1];
394 p
[0] = new Parameter (targs
[0], "p0", Parameter
.Modifier
.NONE
, null, loc
);
396 for (int i
= 1; i
< dyn_args_count
+ 1; ++i
)
397 p
[i
] = new Parameter (targs
[i
], "p" + i
.ToString ("X"), arguments
[i
- 1].Modifier
, null, loc
);
399 TypeContainer parent
= CreateSiteContainer ();
400 Delegate d
= new Delegate (parent
.NamespaceEntry
, parent
, new TypeExpression (TypeManager
.TypeToReflectionType (rt
), loc
),
401 Modifiers
.INTERNAL
| Modifiers
.COMPILER_GENERATED
,
402 new MemberName ("Container" + container_counter
++.ToString ("X")),
403 new ParametersCompiled (p
), null);
408 parent
.AddDelegate (d
);
409 del_type
= new TypeExpression (d
.TypeBuilder
, loc
);
412 TypeExpr site_type
= new GenericTypeExpr (TypeManager
.generic_call_site_type
, new TypeArguments (del_type
), loc
);
416 public static void Reset ()
418 global_site_container
= null;
419 field_counter
= container_counter
= 0;
424 // Dynamic member access compound assignment for events
426 class DynamicEventCompoundAssign
: DynamicExpressionStatement
, IDynamicBinder
429 ExpressionStatement assignment
;
430 ExpressionStatement invoke
;
432 public DynamicEventCompoundAssign (string name
, Arguments args
, ExpressionStatement assignment
, ExpressionStatement invoke
, Location loc
)
433 : base (null, args
, loc
)
436 this.assignment
= assignment
;
437 this.invoke
= invoke
;
440 // Used by += or -= only
441 type
= TypeManager
.bool_type
;
444 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
446 Arguments binder_args
= new Arguments (2);
448 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
449 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
451 return new Invocation (GetBinder ("IsEvent", loc
), binder_args
);
454 public override void EmitStatement (EmitContext ec
)
456 Statement cond
= new If (
457 new Binary (Binary
.Operator
.Equality
, this, new BoolLiteral (true, loc
)),
458 new StatementExpression (invoke
),
459 new StatementExpression (assignment
),
465 class DynamicConversion
: DynamicExpressionStatement
, IDynamicBinder
467 public DynamicConversion (Type targetType
, CSharpBinderFlags flags
, Arguments args
, Location loc
)
468 : base (null, args
, loc
)
475 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
477 Arguments binder_args
= new Arguments (2);
479 flags
|= ec
.HasSet (ResolveContext
.Options
.CheckedScope
) ? CSharpBinderFlags
.CheckedContext
: 0;
481 binder_args
.Add (new Argument (new BinderFlags (flags
, this)));
482 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
483 return new Invocation (GetBinder ("Convert", loc
), binder_args
);
487 class DynamicIndexBinder
: DynamicMemberAssignable
489 public DynamicIndexBinder (Arguments args
, Location loc
)
494 protected override Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
, bool isSet
)
496 Arguments binder_args
= new Arguments (3);
498 binder_args
.Add (new Argument (new BinderFlags (0, this)));
499 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
500 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
502 return new Invocation (GetBinder (isSet
? "SetIndex" : "GetIndex", loc
), binder_args
);
506 class DynamicInvocation
: DynamicExpressionStatement
, IDynamicBinder
508 ATypeNameExpression member
;
510 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Location loc
)
511 : base (null, args
, loc
)
514 this.member
= member
;
517 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Type type
, Location loc
)
518 : this (member
, args
, loc
)
520 // When a return type is known not to be dynamic
524 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
526 Arguments binder_args
= new Arguments (member
!= null ? 5 : 3);
527 bool is_member_access
= member
is MemberAccess
;
529 CSharpBinderFlags call_flags
;
530 if (!is_member_access
&& member
is SimpleName
) {
531 call_flags
= CSharpBinderFlags
.InvokeSimpleName
;
532 is_member_access
= true;
537 binder_args
.Add (new Argument (new BinderFlags (call_flags
, this)));
539 if (is_member_access
)
540 binder_args
.Add (new Argument (new StringLiteral (member
.Name
, member
.Location
)));
542 if (member
!= null && member
.HasTypeArguments
) {
543 TypeArguments ta
= member
.TypeArguments
;
544 if (ta
.Resolve (ec
)) {
545 ArrayList targs
= new ArrayList (ta
.Count
);
546 foreach (Type t
in ta
.Arguments
)
547 targs
.Add (new TypeOf (new TypeExpression (t
, loc
), loc
));
549 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", targs
, loc
)));
551 } else if (is_member_access
) {
552 binder_args
.Add (new Argument (new NullLiteral (loc
)));
555 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
557 Expression real_args
;
559 // Cannot be null because .NET trips over
560 real_args
= new ArrayCreation (new MemberAccess (GetBinderNamespace (loc
), "CSharpArgumentInfo", loc
), "[]", new ArrayList (0), loc
);
562 real_args
= new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
);
565 binder_args
.Add (new Argument (real_args
));
567 return new Invocation (GetBinder (is_member_access
? "InvokeMember" : "Invoke", loc
), binder_args
);
570 public override void EmitStatement (EmitContext ec
)
572 flags
= CSharpBinderFlags
.ResultDiscarded
;
573 base.EmitStatement (ec
);
577 class DynamicMemberBinder
: DynamicMemberAssignable
579 readonly string name
;
581 public DynamicMemberBinder (string name
, Arguments args
, Location loc
)
587 protected override Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
, bool isSet
)
589 Arguments binder_args
= new Arguments (4);
591 binder_args
.Add (new Argument (new BinderFlags (0, this)));
592 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
593 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
594 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
596 return new Invocation (GetBinder (isSet
? "SetMember" : "GetMember", loc
), binder_args
);
601 // Any member binder which can be source and target of assignment
603 abstract class DynamicMemberAssignable
: DynamicExpressionStatement
, IDynamicBinder
, IAssignMethod
606 Arguments setter_args
;
608 protected DynamicMemberAssignable (Arguments args
, Location loc
)
609 : base (null, args
, loc
)
614 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
617 // DoResolve always uses getter
619 return CreateCallSiteBinder (ec
, args
, false);
622 protected abstract Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
, bool isSet
);
624 public override Expression
DoResolveLValue (ResolveContext rc
, Expression right_side
)
626 if (DoResolveCore (rc
)) {
627 setter_args
= new Arguments (Arguments
.Count
+ 1);
628 setter_args
.AddRange (Arguments
);
629 setter_args
.Add (new Argument (right_side
));
630 setter
= CreateCallSiteBinder (rc
, setter_args
, true);
636 #region IAssignMethod Members
638 public void Emit (EmitContext ec
, bool leave_copy
)
640 throw new NotImplementedException ();
643 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
645 EmitCall (ec
, setter
, setter_args
, !leave_copy
);
651 class DynamicUnaryConversion
: DynamicExpressionStatement
, IDynamicBinder
655 public DynamicUnaryConversion (string name
, Arguments args
, Location loc
)
656 : base (null, args
, loc
)
660 if (name
== "IsTrue" || name
== "IsFalse")
661 type
= TypeManager
.bool_type
;
664 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
666 Arguments binder_args
= new Arguments (3);
668 MemberAccess sle
= new MemberAccess (new MemberAccess (
669 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Linq", loc
), "Expressions", loc
);
671 var flags
= ec
.HasSet (ResolveContext
.Options
.CheckedScope
) ? CSharpBinderFlags
.CheckedContext
: 0;
673 binder_args
.Add (new Argument (new BinderFlags (flags
, this)));
674 binder_args
.Add (new Argument (new MemberAccess (new MemberAccess (sle
, "ExpressionType", loc
), name
, loc
)));
675 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
677 return new Invocation (GetBinder ("UnaryOperation", loc
), binder_args
);