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
;
22 class DynamicTypeExpr
: TypeExpr
24 public DynamicTypeExpr (Location loc
)
28 type
= InternalType
.Dynamic
;
29 eclass
= ExprClass
.Type
;
32 public override bool CheckAccessLevel (IMemberContext ds
)
37 protected override TypeExpr
DoResolveAsTypeStep (IMemberContext ec
)
44 // Expression created from runtime dynamic object value
46 public class RuntimeValueExpression
: Expression
, IDynamicAssign
49 public class DynamicMetaObject { public Type RuntimeType; }
52 readonly DynamicMetaObject obj
;
54 public RuntimeValueExpression (DynamicMetaObject obj
)
57 this.type
= obj
.RuntimeType
;
58 this.eclass
= ExprClass
.Variable
;
61 public override Expression
CreateExpressionTree (ResolveContext ec
)
63 throw new NotImplementedException ();
66 public override Expression
DoResolve (ResolveContext ec
)
71 public override Expression
DoResolveLValue (ResolveContext ec
, Expression right_side
)
76 public override void Emit (EmitContext ec
)
78 throw new NotImplementedException ();
81 #region IAssignMethod Members
83 public void Emit (EmitContext ec
, bool leave_copy
)
85 throw new NotImplementedException ();
88 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
90 throw new NotImplementedException ();
96 public SLE
.Expression
MakeAssignExpression (BuilderContext ctx
)
98 return obj
.Expression
;
101 public override SLE
.Expression
MakeExpression (BuilderContext ctx
)
103 return SLE
.Expression
.Convert (obj
.Expression
, type
);
107 public DynamicMetaObject MetaObject
{
112 interface IDynamicBinder
114 Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
);
118 // Extends standard assignment interface for expressions
119 // supported by dynamic resolver
121 interface IDynamicAssign
: IAssignMethod
124 SLE
.Expression
MakeAssignExpression (BuilderContext ctx
);
128 class DynamicExpressionStatement
: ExpressionStatement
130 class StaticDataClass
: CompilerGeneratedClass
132 public StaticDataClass ()
133 : base (new RootDeclSpace (new NamespaceEntry (null, null, null)),
134 new MemberName (CompilerGeneratedClass
.MakeName (null, "c", "DynamicSites", 0)),
135 Modifiers
.INTERNAL
| Modifiers
.STATIC
)
137 ModFlags
&= ~Modifiers
.SEALED
;
141 static StaticDataClass global_site_container
;
142 static int field_counter
;
143 static int container_counter
;
145 readonly Arguments arguments
;
146 protected IDynamicBinder binder
;
147 Expression binder_expr
;
149 public DynamicExpressionStatement (IDynamicBinder binder
, Arguments args
, Location loc
)
151 this.binder
= binder
;
152 this.arguments
= args
;
156 public Arguments Arguments
{
162 static TypeContainer
CreateSiteContainer ()
164 if (global_site_container
== null) {
165 global_site_container
= new StaticDataClass ();
166 RootContext
.ToplevelTypes
.AddCompilerGeneratedClass (global_site_container
);
167 global_site_container
.DefineType ();
168 global_site_container
.Define ();
169 // global_site_container.EmitType ();
171 RootContext
.RegisterCompilerGeneratedType (global_site_container
.TypeBuilder
);
174 return global_site_container
;
177 static Field
CreateSiteField (FullNamedExpression type
)
179 TypeContainer site_container
= CreateSiteContainer ();
180 Field f
= new Field (site_container
, type
, Modifiers
.PUBLIC
| Modifiers
.STATIC
,
181 new MemberName ("Site" + field_counter
++), null);
184 site_container
.AddField (f
);
188 public override Expression
CreateExpressionTree (ResolveContext ec
)
190 throw new NotImplementedException ();
193 public override Expression
DoResolve (ResolveContext ec
)
195 if (eclass
!= ExprClass
.Invalid
)
198 if (TypeManager
.call_site_type
== null)
199 TypeManager
.call_site_type
= TypeManager
.CoreLookupType (ec
.Compiler
,
200 "System.Runtime.CompilerServices", "CallSite", Kind
.Class
, true);
202 if (TypeManager
.generic_call_site_type
== null)
203 TypeManager
.generic_call_site_type
= TypeManager
.CoreLookupType (ec
.Compiler
,
204 "System.Runtime.CompilerServices", "CallSite`1", Kind
.Class
, true);
206 eclass
= ExprClass
.Value
;
209 type
= InternalType
.Dynamic
;
211 binder_expr
= binder
.CreateCallSiteBinder (ec
, arguments
);
215 public override void Emit (EmitContext ec
)
220 public override void EmitStatement (EmitContext ec
)
222 // All C# created payloads require to have object convertible
223 // return type even if we know it won't be used
225 ec
.ig
.Emit (OpCodes
.Pop
);
228 void EmitCall (EmitContext ec
)
230 int dyn_args_count
= arguments
== null ? 0 : arguments
.Count
;
231 TypeExpr site_type
= CreateSiteType (RootContext
.ToplevelTypes
.Compiler
, dyn_args_count
);
232 FieldExpr site_field_expr
= new FieldExpr (CreateSiteField (site_type
).FieldBuilder
, loc
);
234 SymbolWriter
.OpenCompilerGeneratedBlock (ec
.ig
);
236 Arguments args
= new Arguments (1);
237 args
.Add (new Argument (binder_expr
));
238 StatementExpression s
= new StatementExpression (new SimpleAssign (site_field_expr
, new Invocation (new MemberAccess (site_type
, "Create"), args
)));
240 BlockContext bc
= new BlockContext (ec
.MemberContext
, null, TypeManager
.void_type
);
241 if (s
.Resolve (bc
)) {
242 Statement init
= new If (new Binary (Binary
.Operator
.Equality
, site_field_expr
, new NullLiteral (loc
)), s
, loc
);
246 args
= new Arguments (1 + dyn_args_count
);
247 args
.Add (new Argument (site_field_expr
));
248 if (arguments
!= null) {
249 foreach (Argument a
in arguments
) {
250 if (a
is NamedArgument
) {
251 // Name is not valid in this context
252 args
.Add (new Argument (a
.Expr
, a
.ArgType
));
260 Expression target
= new DelegateInvocation (new MemberAccess (site_field_expr
, "Target", loc
).Resolve (bc
), args
, loc
).Resolve (bc
);
264 SymbolWriter
.CloseCompilerGeneratedBlock (ec
.ig
);
267 public static MemberAccess
GetBinderNamespace (Location loc
)
269 return new MemberAccess (new MemberAccess (
270 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "Microsoft", loc
), "CSharp", loc
), "RuntimeBinder", loc
);
273 TypeExpr
CreateSiteType (CompilerContext ctx
, int dyn_args_count
)
275 const int default_args
= 2;
277 bool has_ref_out_argument
= false;
278 FullNamedExpression
[] targs
= new FullNamedExpression
[dyn_args_count
+ default_args
];
279 targs
[0] = new TypeExpression (TypeManager
.call_site_type
, loc
);
280 for (int i
= 0; i
< dyn_args_count
; ++i
) {
282 Argument a
= arguments
[i
];
283 if (a
.Type
== TypeManager
.null_type
)
284 arg_type
= TypeManager
.object_type
;
286 arg_type
= TypeManager
.TypeToReflectionType (a
.Type
);
288 if (a
.ArgType
== Argument
.AType
.Out
|| a
.ArgType
== Argument
.AType
.Ref
)
289 has_ref_out_argument
= true;
291 targs
[i
+ 1] = new TypeExpression (arg_type
, loc
);
294 TypeExpr del_type
= null;
295 if (!has_ref_out_argument
) {
296 Type t
= TypeManager
.CoreLookupType (ctx
, "System", "Func`" + (dyn_args_count
+ default_args
), Kind
.Delegate
, false);
298 targs
[targs
.Length
- 1] = new TypeExpression (TypeManager
.TypeToReflectionType (type
), loc
);
299 del_type
= new GenericTypeExpr (t
, new TypeArguments (targs
), loc
);
303 // No appropriate predefined delegate found
304 if (del_type
== null) {
305 Parameter
[] p
= new Parameter
[dyn_args_count
+ 1];
306 p
[0] = new Parameter (targs
[0], "p0", Parameter
.Modifier
.NONE
, null, loc
);
308 for (int i
= 1; i
< dyn_args_count
+ 1; ++i
)
309 p
[i
] = new Parameter (targs
[i
], "p" + i
.ToString ("X"), arguments
[i
- 1].Modifier
, null, loc
);
311 TypeContainer parent
= CreateSiteContainer ();
312 Delegate d
= new Delegate (parent
.NamespaceEntry
, parent
, new TypeExpression (TypeManager
.TypeToReflectionType (type
), loc
),
313 Modifiers
.INTERNAL
| Modifiers
.COMPILER_GENERATED
,
314 new MemberName ("Container" + container_counter
++.ToString ("X")),
315 new ParametersCompiled (p
), null);
320 parent
.AddDelegate (d
);
321 del_type
= new TypeExpression (d
.TypeBuilder
, loc
);
324 TypeExpr site_type
= new GenericTypeExpr (TypeManager
.generic_call_site_type
, new TypeArguments (del_type
), loc
);
328 public static void Reset ()
330 global_site_container
= null;
331 field_counter
= container_counter
= 0;
336 // Dynamic member access compound assignment for events
338 class DynamicEventCompoundAssign
: DynamicExpressionStatement
, IDynamicBinder
341 ExpressionStatement assignment
;
342 ExpressionStatement invoke
;
344 public DynamicEventCompoundAssign (string name
, Arguments args
, ExpressionStatement assignment
, ExpressionStatement invoke
, Location loc
)
345 : base (null, args
, loc
)
348 this.assignment
= assignment
;
349 this.invoke
= invoke
;
352 // Used by += or -= only
353 type
= TypeManager
.bool_type
;
356 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
358 Arguments binder_args
= new Arguments (2);
359 MemberAccess binder
= GetBinderNamespace (loc
);
361 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
362 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
364 return new New (new MemberAccess (binder
, "CSharpIsEventBinder", loc
), binder_args
, loc
);
367 public override void EmitStatement (EmitContext ec
)
369 Statement cond
= new If (
370 new Binary (Binary
.Operator
.Equality
, this, new BoolLiteral (true, loc
)),
371 new StatementExpression (invoke
),
372 new StatementExpression (assignment
),
378 class DynamicConversion
: DynamicExpressionStatement
, IDynamicBinder
382 public DynamicConversion (Type targetType
, bool isExplicit
, Arguments args
, Location loc
)
383 : base (null, args
, loc
)
386 is_explicit
= isExplicit
;
390 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
392 Arguments binder_args
= new Arguments (2);
393 MemberAccess binder
= GetBinderNamespace (loc
);
395 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
396 binder_args
.Add (new Argument (new MemberAccess (new MemberAccess (binder
, "CSharpConversionKind", loc
),
397 is_explicit
? "ExplicitConversion" : "ImplicitConversion", loc
)));
398 binder_args
.Add (new Argument (new BoolLiteral (ec
.HasSet (ResolveContext
.Options
.CheckedScope
), loc
)));
400 return new New (new MemberAccess (binder
, "CSharpConvertBinder", loc
), binder_args
, loc
);
404 class DynamicIndexBinder
: DynamicExpressionStatement
, IDynamicBinder
, IAssignMethod
408 public DynamicIndexBinder (bool isSet
, Arguments args
, Location loc
)
409 : base (null, args
, loc
)
415 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
417 Arguments binder_args
= new Arguments (2);
418 MemberAccess binder
= GetBinderNamespace (loc
);
420 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
421 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
423 return new New (new MemberAccess (binder
, isSet
? "CSharpSetIndexBinder" : "CSharpGetIndexBinder", loc
), binder_args
, loc
);
426 #region IAssignMethod Members
428 public void Emit (EmitContext ec
, bool leave_copy
)
430 throw new NotImplementedException ();
433 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
444 class DynamicInvocation
: DynamicExpressionStatement
, IDynamicBinder
446 ATypeNameExpression member
;
448 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Location loc
)
449 : base (null, args
, loc
)
452 this.member
= member
;
455 public DynamicInvocation (ATypeNameExpression member
, Arguments args
, Type type
, Location loc
)
456 : this (member
, args
, loc
)
458 // When a return type is known not to be dynamic
462 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
464 Arguments binder_args
= new Arguments (member
!= null ? 5 : 3);
465 MemberAccess binder
= GetBinderNamespace (loc
);
466 bool is_member_access
= member
is MemberAccess
;
469 if (!is_member_access
&& member
is SimpleName
) {
470 call_flags
= "SimpleNameCall";
471 is_member_access
= true;
476 binder_args
.Add (new Argument (new MemberAccess (new MemberAccess (binder
, "CSharpCallFlags", loc
), call_flags
, loc
)));
478 if (is_member_access
)
479 binder_args
.Add (new Argument (new StringLiteral (member
.Name
, member
.Location
)));
481 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
483 if (member
!= null && member
.HasTypeArguments
) {
484 TypeArguments ta
= member
.TypeArguments
;
485 if (ta
.Resolve (ec
)) {
486 ArrayList targs
= new ArrayList (ta
.Count
);
487 foreach (Type t
in ta
.Arguments
)
488 targs
.Add (new TypeOf (new TypeExpression (t
, loc
), loc
));
490 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", targs
, loc
)));
492 } else if (is_member_access
) {
493 binder_args
.Add (new Argument (new NullLiteral (loc
)));
496 Expression real_args
;
498 // Cannot be null because .NET trips over
499 real_args
= new ArrayCreation (new MemberAccess (binder
, "CSharpArgumentInfo", loc
), "[]", new ArrayList (0), loc
);
501 real_args
= new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
);
504 binder_args
.Add (new Argument (real_args
));
506 return new New (new MemberAccess (binder
,
507 is_member_access
? "CSharpInvokeMemberBinder" : "CSharpInvokeBinder", loc
), binder_args
, loc
);
511 class DynamicMemberBinder
: DynamicExpressionStatement
, IDynamicBinder
, IAssignMethod
514 readonly string name
;
516 public DynamicMemberBinder (bool isSet
, string name
, Arguments args
, Location loc
)
517 : base (null, args
, loc
)
524 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
526 Arguments binder_args
= new Arguments (3);
527 MemberAccess binder
= GetBinderNamespace (loc
);
529 binder_args
.Add (new Argument (new StringLiteral (name
, loc
)));
530 binder_args
.Add (new Argument (new TypeOf (new TypeExpression (ec
.CurrentType
, loc
), loc
)));
531 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
533 return new New (new MemberAccess (binder
, isSet
? "CSharpSetMemberBinder" : "CSharpGetMemberBinder", loc
), binder_args
, loc
);
536 #region IAssignMethod Members
538 public void Emit (EmitContext ec
, bool leave_copy
)
540 throw new NotImplementedException ();
543 public void EmitAssign (EmitContext ec
, Expression source
, bool leave_copy
, bool prepare_for_load
)
554 class DynamicUnaryConversion
: DynamicExpressionStatement
, IDynamicBinder
558 public DynamicUnaryConversion (string name
, Arguments args
, Location loc
)
559 : base (null, args
, loc
)
563 if (name
== "IsTrue" || name
== "IsFalse")
564 type
= TypeManager
.bool_type
;
567 public Expression
CreateCallSiteBinder (ResolveContext ec
, Arguments args
)
569 Arguments binder_args
= new Arguments (3);
571 MemberAccess sle
= new MemberAccess (new MemberAccess (
572 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Linq", loc
), "Expressions", loc
);
574 MemberAccess binder
= GetBinderNamespace (loc
);
576 binder_args
.Add (new Argument (new MemberAccess (new MemberAccess (sle
, "ExpressionType", loc
), name
, loc
)));
577 binder_args
.Add (new Argument (new BoolLiteral (ec
.HasSet (ResolveContext
.Options
.CheckedScope
), loc
)));
578 binder_args
.Add (new Argument (new ImplicitlyTypedArrayCreation ("[]", args
.CreateDynamicBinderArguments (), loc
)));
580 return new New (new MemberAccess (binder
, "CSharpUnaryOperationBinder", loc
), binder_args
, loc
);