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
{
134 interface IDynamicBinder
136 Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
);
140 // Extends standard assignment interface for expressions
141 // supported by dynamic resolver
143 interface IDynamicAssign
: IAssignMethod
146 SLE
.Expression
MakeAssignExpression (BuilderContext ctx
);
150 class DynamicExpressionStatement
: ExpressionStatement
152 class StaticDataClass
: CompilerGeneratedClass
154 public StaticDataClass ()
155 : base (new RootDeclSpace (new NamespaceEntry (null, null, null)),
156 new MemberName (CompilerGeneratedClass
.MakeName (null, "c", "DynamicSites", 0)),
157 Modifiers
.INTERNAL
| Modifiers
.STATIC
)
159 ModFlags
&= ~Modifiers
.SEALED
;
164 // Binder flag dynamic constant, the value is combination of
165 // flags known at resolve stage and flags known only at emit
168 protected class BinderFlags
: EnumConstant
170 DynamicExpressionStatement statement
;
171 CSharpBinderFlags flags
;
173 public BinderFlags (CSharpBinderFlags flags
, DynamicExpressionStatement statement
)
177 this.statement
= statement
;
180 public override Expression
DoResolve (ResolveContext ec
)
182 Child
= new IntConstant ((int) (flags
| statement
.flags
), statement
.loc
);
184 type
= TypeManager
.binder_flags
;
185 eclass
= Child
.eclass
;
190 static StaticDataClass global_site_container
;
191 static int field_counter
;
192 static int container_counter
;
194 readonly Arguments arguments
;
195 protected IDynamicBinder binder
;
196 Expression binder_expr
;
198 // Used by BinderFlags
199 protected CSharpBinderFlags flags
;
201 public DynamicExpressionStatement (IDynamicBinder binder
, Arguments args
, Location loc
)
203 this.binder
= binder
;
204 this.arguments
= args
;
208 public Arguments Arguments
{
214 static TypeContainer
CreateSiteContainer ()
216 if (global_site_container
== null) {
217 global_site_container
= new StaticDataClass ();
218 RootContext
.ToplevelTypes
.AddCompilerGeneratedClass (global_site_container
);
219 global_site_container
.DefineType ();
220 global_site_container
.Define ();
221 // global_site_container.EmitType ();
223 RootContext
.RegisterCompilerGeneratedType (global_site_container
.TypeBuilder
);
226 return global_site_container
;
229 static Field
CreateSiteField (FullNamedExpression type
)
231 TypeContainer site_container
= CreateSiteContainer ();
232 Field f
= new Field (site_container
, type
, Modifiers
.PUBLIC
| Modifiers
.STATIC
,
233 new MemberName ("Site" + field_counter
++), null);
236 site_container
.AddField (f
);
240 public override Expression
CreateExpressionTree (ResolveContext ec
)
242 throw new NotImplementedException ();
245 public override Expression
DoResolve (ResolveContext ec
)
247 if (eclass
!= ExprClass
.Invalid
)
250 if (TypeManager
.call_site_type
== null)
251 TypeManager
.call_site_type
= TypeManager
.CoreLookupType (ec
.Compiler
,
252 "System.Runtime.CompilerServices", "CallSite", Kind
.Class
, true);
254 if (TypeManager
.generic_call_site_type
== null)
255 TypeManager
.generic_call_site_type
= TypeManager
.CoreLookupType (ec
.Compiler
,
256 "System.Runtime.CompilerServices", "CallSite`1", Kind
.Class
, true);
258 if (TypeManager
.binder_type
== null) {
259 var t
= TypeManager
.CoreLookupType (ec
.Compiler
,
260 "Microsoft.CSharp.RuntimeBinder", "Binder", Kind
.Class
, true);
262 TypeManager
.binder_type
= new TypeExpression (t
, Location
.Null
);
265 if (TypeManager
.binder_flags
== null) {
266 TypeManager
.binder_flags
= TypeManager
.CoreLookupType (ec
.Compiler
,
267 "Microsoft.CSharp.RuntimeBinder", "CSharpBinderFlags", Kind
.Enum
, true);
270 eclass
= ExprClass
.Value
;
273 type
= InternalType
.Dynamic
;
275 // TODO: If any core type fails to load, skip this ?
276 binder_expr
= binder
.CreateCallSiteBinder (ec
, arguments
);
280 public override void Emit (EmitContext ec
)
282 EmitCall (ec
, false);
285 public override void EmitStatement (EmitContext ec
)
290 void EmitCall (EmitContext ec
, bool isStatement
)
292 int dyn_args_count
= arguments
== null ? 0 : arguments
.Count
;
293 TypeExpr site_type
= CreateSiteType (RootContext
.ToplevelTypes
.Compiler
, dyn_args_count
, isStatement
);
294 FieldExpr site_field_expr
= new FieldExpr (CreateSiteField (site_type
).FieldBuilder
, loc
);
296 SymbolWriter
.OpenCompilerGeneratedBlock (ec
.ig
);
298 Arguments args
= new Arguments (1);
299 args
.Add (new Argument (binder_expr
));
300 StatementExpression s
= new StatementExpression (new SimpleAssign (site_field_expr
, new Invocation (new MemberAccess (site_type
, "Create"), args
)));
302 BlockContext bc
= new BlockContext (ec
.MemberContext
, null, TypeManager
.void_type
);
303 if (s
.Resolve (bc
)) {
304 Statement init
= new If (new Binary (Binary
.Operator
.Equality
, site_field_expr
, new NullLiteral (loc
)), s
, loc
);
308 args
= new Arguments (1 + dyn_args_count
);
309 args
.Add (new Argument (site_field_expr
));
310 if (arguments
!= null) {
311 foreach (Argument a
in arguments
) {
312 if (a
is NamedArgument
) {
313 // Name is not valid in this context
314 args
.Add (new Argument (a
.Expr
, a
.ArgType
));
322 Expression target
= new DelegateInvocation (new MemberAccess (site_field_expr
, "Target", loc
).Resolve (bc
), args
, loc
).Resolve (bc
);
326 SymbolWriter
.CloseCompilerGeneratedBlock (ec
.ig
);
329 public static MemberAccess
GetBinderNamespace (Location loc
)
331 return new MemberAccess (new MemberAccess (
332 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "Microsoft", loc
), "CSharp", loc
), "RuntimeBinder", loc
);
335 public static MemberAccess
GetBinder (string name
, Location loc
)
337 return new MemberAccess (TypeManager
.binder_type
, name
, loc
);
340 TypeExpr
CreateSiteType (CompilerContext ctx
, int dyn_args_count
, bool is_statement
)
342 int default_args
= is_statement
? 1 : 2;
344 bool has_ref_out_argument
= false;
345 FullNamedExpression
[] targs
= new FullNamedExpression
[dyn_args_count
+ default_args
];
346 targs
[0] = new TypeExpression (TypeManager
.call_site_type
, loc
);
347 for (int i
= 0; i
< dyn_args_count
; ++i
) {
349 Argument a
= arguments
[i
];
350 if (a
.Type
== TypeManager
.null_type
)
351 arg_type
= TypeManager
.object_type
;
353 arg_type
= TypeManager
.TypeToReflectionType (a
.Type
);
355 if (a
.ArgType
== Argument
.AType
.Out
|| a
.ArgType
== Argument
.AType
.Ref
)
356 has_ref_out_argument
= true;
358 targs
[i
+ 1] = new TypeExpression (arg_type
, loc
);
361 TypeExpr del_type
= null;
362 if (!has_ref_out_argument
) {
363 string d_name
= is_statement
? "Action`" : "Func`";
365 Type t
= TypeManager
.CoreLookupType (ctx
, "System", d_name
+ (dyn_args_count
+ default_args
), Kind
.Delegate
, false);
368 targs
[targs
.Length
- 1] = new TypeExpression (TypeManager
.TypeToReflectionType (type
), loc
);
370 del_type
= new GenericTypeExpr (t
, new TypeArguments (targs
), loc
);
374 // No appropriate predefined delegate found
375 if (del_type
== null) {
376 Type rt
= is_statement
? TypeManager
.void_type
: type
;
377 Parameter
[] p
= new Parameter
[dyn_args_count
+ 1];
378 p
[0] = new Parameter (targs
[0], "p0", Parameter
.Modifier
.NONE
, null, loc
);
380 for (int i
= 1; i
< dyn_args_count
+ 1; ++i
)
381 p
[i
] = new Parameter (targs
[i
], "p" + i
.ToString ("X"), arguments
[i
- 1].Modifier
, null, loc
);
383 TypeContainer parent
= CreateSiteContainer ();
384 Delegate d
= new Delegate (parent
.NamespaceEntry
, parent
, new TypeExpression (TypeManager
.TypeToReflectionType (rt
), loc
),
385 Modifiers
.INTERNAL
| Modifiers
.COMPILER_GENERATED
,
386 new MemberName ("Container" + container_counter
++.ToString ("X")),
387 new ParametersCompiled (p
), null);
392 parent
.AddDelegate (d
);
393 del_type
= new TypeExpression (d
.TypeBuilder
, loc
);
396 TypeExpr site_type
= new GenericTypeExpr (TypeManager
.generic_call_site_type
, new TypeArguments (del_type
), loc
);
400 public static void Reset ()
402 global_site_container
= null;
403 field_counter
= container_counter
= 0;
408 // Dynamic member access compound assignment for events
410 class DynamicEventCompoundAssign
: DynamicExpressionStatement
, IDynamicBinder
413 ExpressionStatement assignment
;
414 ExpressionStatement invoke
;
416 public DynamicEventCompoundAssign (string name
, Arguments args
, ExpressionStatement assignment
, ExpressionStatement invoke
, Location loc
)
417 : base (null, args
, loc
)
420 this.assignment
= assignment
;
421 this.invoke
= invoke
;
424 // Used by += or -= only
425 type
= TypeManager
.bool_type
;
428 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
430 Arguments binder_args
= new Arguments (2);
432 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
433 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
435 return new Invocation (GetBinder ("IsEvent", loc
), binder_args
);
438 public override void EmitStatement (EmitContext ec
)
440 Statement cond
= new If (
441 new Binary (Binary
.Operator
.Equality
, this, new BoolLiteral (true, loc
)),
442 new StatementExpression (invoke
),
443 new StatementExpression (assignment
),
449 class DynamicConversion
: DynamicExpressionStatement
, IDynamicBinder
451 public DynamicConversion (Type targetType
, CSharpBinderFlags flags
, Arguments args
, Location loc
)
452 : base (null, args
, loc
)
459 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
461 Arguments binder_args
= new Arguments (2);
463 flags
|= ec
.HasSet (ResolveContext
.Options
.CheckedScope
) ? CSharpBinderFlags
.CheckedContext
: 0;
465 binder_args
.Add (new Argument (new BinderFlags (flags
, this)));
466 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
467 return new Invocation (GetBinder ("Convert", loc
), binder_args
);
471 class DynamicIndexBinder
: DynamicExpressionStatement
, IDynamicBinder
, IAssignMethod
475 public DynamicIndexBinder (bool isSet
, Arguments args
, Location loc
)
476 : base (null, args
, loc
)
482 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
484 Arguments binder_args
= new Arguments (3);
486 binder_args
.Add (new Argument (new BinderFlags (0, this)));
487 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
488 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
490 return new Invocation (GetBinder (isSet
? "SetIndex" : "GetIndex", loc
), binder_args
);
493 #region IAssignMethod Members
495 public void Emit (EmitContext ec
, bool leave_copy
)
497 throw new NotImplementedException ();
500 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
511 class DynamicInvocation
: DynamicExpressionStatement
, IDynamicBinder
513 ATypeNameExpression member
;
515 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Location loc
)
516 : base (null, args
, loc
)
519 this.member
= member
;
522 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Type type
, Location loc
)
523 : this (member
, args
, loc
)
525 // When a return type is known not to be dynamic
529 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
531 Arguments binder_args
= new Arguments (member
!= null ? 5 : 3);
532 bool is_member_access
= member
is MemberAccess
;
534 CSharpBinderFlags call_flags
;
535 if (!is_member_access
&& member
is SimpleName
) {
536 call_flags
= CSharpBinderFlags
.InvokeSimpleName
;
537 is_member_access
= true;
542 binder_args
.Add (new Argument (new BinderFlags (call_flags
, this)));
544 if (is_member_access
)
545 binder_args
.Add (new Argument (new StringLiteral (member
.Name
, member
.Location
)));
547 if (member
!= null && member
.HasTypeArguments
) {
548 TypeArguments ta
= member
.TypeArguments
;
549 if (ta
.Resolve (ec
)) {
550 ArrayList targs
= new ArrayList (ta
.Count
);
551 foreach (Type t
in ta
.Arguments
)
552 targs
.Add (new TypeOf (new TypeExpression (t
, loc
), loc
));
554 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", targs
, loc
)));
556 } else if (is_member_access
) {
557 binder_args
.Add (new Argument (new NullLiteral (loc
)));
560 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
562 Expression real_args
;
564 // Cannot be null because .NET trips over
565 real_args
= new ArrayCreation (new MemberAccess (GetBinderNamespace (loc
), "CSharpArgumentInfo", loc
), "[]", new ArrayList (0), loc
);
567 real_args
= new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
);
570 binder_args
.Add (new Argument (real_args
));
572 return new Invocation (GetBinder (is_member_access
? "InvokeMember" : "Invoke", loc
), binder_args
);
575 public override void EmitStatement (EmitContext ec
)
577 flags
= CSharpBinderFlags
.ResultDiscarded
;
578 base.EmitStatement (ec
);
582 class DynamicMemberBinder
: DynamicExpressionStatement
, IDynamicBinder
, IAssignMethod
585 readonly string name
;
587 public DynamicMemberBinder (bool isSet
, string name
, Arguments args
, Location loc
)
588 : base (null, args
, loc
)
595 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
597 Arguments binder_args
= new Arguments (4);
599 binder_args
.Add (new Argument (new BinderFlags (0, this)));
600 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
601 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
602 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
604 return new Invocation (GetBinder (isSet
? "SetMember" : "GetMember", loc
), binder_args
);
607 #region IAssignMethod Members
609 public void Emit (EmitContext ec
, bool leave_copy
)
611 throw new NotImplementedException ();
614 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
625 class DynamicUnaryConversion
: DynamicExpressionStatement
, IDynamicBinder
629 public DynamicUnaryConversion (string name
, Arguments args
, Location loc
)
630 : base (null, args
, loc
)
634 if (name
== "IsTrue" || name
== "IsFalse")
635 type
= TypeManager
.bool_type
;
638 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
640 Arguments binder_args
= new Arguments (3);
642 MemberAccess sle
= new MemberAccess (new MemberAccess (
643 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Linq", loc
), "Expressions", loc
);
645 var flags
= ec
.HasSet (ResolveContext
.Options
.CheckedScope
) ? CSharpBinderFlags
.CheckedContext
: 0;
647 binder_args
.Add (new Argument (new BinderFlags (flags
, this)));
648 binder_args
.Add (new Argument (new MemberAccess (new MemberAccess (sle
, "ExpressionType", loc
), name
, loc
)));
649 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
651 return new Invocation (GetBinder ("UnaryOperation", loc
), binder_args
);