Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Actions / CallSite.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 #if !FEATURE_CORE_DLR
17 using Microsoft.Scripting.Ast;
18 using Microsoft.Scripting.Ast.Compiler;
19 using Microsoft.Scripting.Utils;
20 #else
21 using System.Linq.Expressions;
22 using System.Linq.Expressions.Compiler;
23 #endif
24
25 using System.Collections.Generic;
26 using System.Collections.ObjectModel;
27 using System.Diagnostics;
28 using System.Dynamic;
29 using System.Dynamic.Utils;
30 using System.Reflection;
31
32 namespace System.Runtime.CompilerServices {
33
34     //
35     // A CallSite provides a fast mechanism for call-site caching of dynamic dispatch
36     // behvaior. Each site will hold onto a delegate that provides a fast-path dispatch
37     // based on previous types that have been seen at the call-site. This delegate will
38     // call UpdateAndExecute if it is called with types that it hasn't seen before.
39     // Updating the binding will typically create (or lookup) a new delegate
40     // that supports fast-paths for both the new type and for any types that 
41     // have been seen previously.
42     // 
43     // DynamicSites will generate the fast-paths specialized for sets of runtime argument
44     // types. However, they will generate exactly the right amount of code for the types
45     // that are seen in the program so that int addition will remain as fast as it would
46     // be with custom implementation of the addition, and the user-defined types can be
47     // as fast as ints because they will all have the same optimal dynamically generated
48     // fast-paths.
49     // 
50     // DynamicSites don't encode any particular caching policy, but use their
51     // CallSiteBinding to encode a caching policy.
52     //
53
54
55     /// <summary>
56     /// A Dynamic Call Site base class. This type is used as a parameter type to the
57     /// dynamic site targets. The first parameter of the delegate (T) below must be
58     /// of this type.
59     /// </summary>
60     public class CallSite {
61
62         // Cache of CallSite constructors for a given delegate type
63         private static CacheDict<Type, Func<CallSiteBinder, CallSite>> _SiteCtors;
64
65         /// <summary>
66         /// The Binder responsible for binding operations at this call site.
67         /// This binder is invoked by the UpdateAndExecute below if all Level 0,
68         /// Level 1 and Level 2 caches experience cache miss.
69         /// </summary>
70         internal readonly CallSiteBinder _binder;
71
72         // only CallSite<T> derives from this
73         internal CallSite(CallSiteBinder binder) {
74             _binder = binder;
75         }
76
77         /// <summary>
78         /// used by Matchmaker sites to indicate rule match.
79         /// </summary>
80         internal bool _match;
81
82         /// <summary>
83         /// Class responsible for binding dynamic operations on the dynamic site.
84         /// </summary>
85         public CallSiteBinder Binder {
86             get { return _binder; }
87         }
88
89         /// <summary>
90         /// Creates a CallSite with the given delegate type and binder.
91         /// </summary>
92         /// <param name="delegateType">The CallSite delegate type.</param>
93         /// <param name="binder">The CallSite binder.</param>
94         /// <returns>The new CallSite.</returns>
95         public static CallSite Create(Type delegateType, CallSiteBinder binder) {
96             ContractUtils.RequiresNotNull(delegateType, "delegateType");
97             ContractUtils.RequiresNotNull(binder, "binder");
98             if (!delegateType.IsSubclassOf(typeof(MulticastDelegate))) throw Error.TypeMustBeDerivedFromSystemDelegate();
99
100             if (_SiteCtors == null) {
101                 // It's okay to just set this, worst case we're just throwing away some data
102                 _SiteCtors = new CacheDict<Type, Func<CallSiteBinder, CallSite>>(100);
103             }
104             Func<CallSiteBinder, CallSite> ctor;
105
106             MethodInfo method = null;
107             var ctors = _SiteCtors;
108             lock (ctors) {
109                 if (!ctors.TryGetValue(delegateType, out ctor)) {
110                     method = typeof(CallSite<>).MakeGenericType(delegateType).GetMethod("Create");
111
112                     if (TypeUtils.CanCache(delegateType)) {
113                         ctor = (Func<CallSiteBinder, CallSite>)Delegate.CreateDelegate(typeof(Func<CallSiteBinder, CallSite>), method);
114                         ctors.Add(delegateType, ctor);
115                     }
116                 }
117             }
118             if (ctor != null) {
119                 return ctor(binder);
120             }
121
122             // slow path
123             return (CallSite)method.Invoke(null, new object[] { binder });
124         }
125     }
126
127     /// <summary>
128     /// Dynamic site type.
129     /// </summary>
130     /// <typeparam name="T">The delegate type.</typeparam>
131     public partial class CallSite<T> : CallSite where T : class {
132         /// <summary>
133         /// The update delegate. Called when the dynamic site experiences cache miss.
134         /// </summary>
135         /// <returns>The update delegate.</returns>
136         public T Update {
137             get {
138                 // if this site is set up for match making, then use NoMatch as an Update 
139                 if (_match) {
140                     Debug.Assert(_CachedNoMatch != null, "all normal sites should have Update cached once there is an instance.");
141                     return _CachedNoMatch;
142                 } else {
143                     Debug.Assert(_CachedUpdate != null, "all normal sites should have Update cached once there is an instance.");
144                     return _CachedUpdate;
145                 }
146             }
147         }
148
149         /// <summary>
150         /// The Level 0 cache - a delegate specialized based on the site history.
151         /// </summary>
152         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
153         public T Target;
154
155
156         /// <summary>
157         /// The Level 1 cache - a history of the dynamic site.
158         /// </summary>
159         internal T[] Rules;
160
161
162         // Cached update delegate for all sites with a given T
163         private static T _CachedUpdate;
164
165         // Cached noMatch delegate for all sites with a given T
166         private static T _CachedNoMatch;
167
168         private CallSite(CallSiteBinder binder)
169             : base(binder) {
170             Target = GetUpdateDelegate();
171         }
172
173         private CallSite()
174             : base(null) {
175         }
176
177         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
178         internal CallSite<T> CreateMatchMaker() {
179             return new CallSite<T>();
180         }
181
182         /// <summary>
183         /// Creates an instance of the dynamic call site, initialized with the binder responsible for the
184         /// runtime binding of the dynamic operations at this call site.
185         /// </summary>
186         /// <param name="binder">The binder responsible for the runtime binding of the dynamic operations at this call site.</param>
187         /// <returns>The new instance of dynamic call site.</returns>
188         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
189         public static CallSite<T> Create(CallSiteBinder binder) {
190             if (!typeof(T).IsSubclassOf(typeof(MulticastDelegate))) throw Error.TypeMustBeDerivedFromSystemDelegate();
191             return new CallSite<T>(binder);
192         }
193
194         private T GetUpdateDelegate() {
195             // This is intentionally non-static to speed up creation - in particular MakeUpdateDelegate
196             // as static generic methods are more expensive than instance methods.  We call a ref helper
197             // so we only access the generic static field once.
198             return GetUpdateDelegate(ref _CachedUpdate);
199         }
200
201         private T GetUpdateDelegate(ref T addr) {
202             if (addr == null) {
203                 // reduce creation cost by not using Interlocked.CompareExchange.  Calling I.CE causes
204                 // us to spend 25% of our creation time in JIT_GenericHandle.  Instead we'll rarely
205                 // create 2 delegates with no other harm caused.
206                 addr = MakeUpdateDelegate();
207             }
208             return addr;
209         }
210
211         /// <summary>
212         /// Clears the rule cache ... used by the call site tests.
213         /// </summary>
214         private void ClearRuleCache() {
215             // make sure it initialized/atomized etc...
216             Binder.GetRuleCache<T>();
217
218             var cache = Binder.Cache;
219
220             if (cache != null) {
221                 lock (cache) {
222                     cache.Clear();
223                 }
224             }
225         }
226
227         const int MaxRules = 10;
228         internal void AddRule(T newRule) {
229             T[] rules = Rules;
230             if (rules == null) {
231                 Rules = new[] { newRule };
232                 return;
233             }
234
235             T[] temp;
236             if (rules.Length < (MaxRules - 1)) {
237                 temp = new T[rules.Length + 1];
238                 Array.Copy(rules, 0, temp, 1, rules.Length);
239             } else {
240                 temp = new T[MaxRules];
241                 Array.Copy(rules, 0, temp, 1, MaxRules - 1);
242             }
243             temp[0] = newRule;
244             Rules = temp;
245         }
246
247         // moves rule +2 up.
248         internal void MoveRule(int i) {
249             var rules = Rules;
250             var rule = rules[i];
251
252             rules[i] = rules[i - 1];
253             rules[i - 1] = rules[i - 2];
254             rules[i - 2] = rule;
255         }
256
257         internal T MakeUpdateDelegate() {
258             Type target = typeof(T);
259             Type[] args;
260             MethodInfo invoke = target.GetMethod("Invoke");
261
262
263             if (target.IsGenericType && IsSimpleSignature(invoke, out args)) {
264                 MethodInfo method = null;
265                 MethodInfo noMatchMethod = null;
266
267                 if (invoke.ReturnType == typeof(void)) {
268                     if (target == DelegateHelpers.GetActionType(args.AddFirst(typeof(CallSite)))) {
269                         method = typeof(UpdateDelegates).GetMethod("UpdateAndExecuteVoid" + args.Length, BindingFlags.NonPublic | BindingFlags.Static);
270                         noMatchMethod = typeof(UpdateDelegates).GetMethod("NoMatchVoid" + args.Length, BindingFlags.NonPublic | BindingFlags.Static);
271                     }
272                 } else {
273                     if (target == DelegateHelpers.GetFuncType(args.AddFirst(typeof(CallSite)))) {
274                         method = typeof(UpdateDelegates).GetMethod("UpdateAndExecute" + (args.Length - 1), BindingFlags.NonPublic | BindingFlags.Static);
275                         noMatchMethod = typeof(UpdateDelegates).GetMethod("NoMatch" + (args.Length - 1), BindingFlags.NonPublic | BindingFlags.Static);
276                     }
277                 }
278                 if (method != null) {
279                     _CachedNoMatch = (T)(object)CreateDelegateHelper(target, noMatchMethod.MakeGenericMethod(args));
280                     return (T)(object)CreateDelegateHelper(target, method.MakeGenericMethod(args));
281                 }
282             }
283
284             _CachedNoMatch = CreateCustomNoMatchDelegate(invoke);
285             return CreateCustomUpdateDelegate(invoke);
286         }
287
288         // NEEDS SECURITY REVIEW:
289         //
290         // This needs to be SafeCritical on Silverlight to allow access to
291         // internal types from user code as generic parameters.
292         //
293         // It's safe for a few reasons:
294         //   1. The internal types are coming from a lower trust level (app code)
295         //   2. We got the internal types from our own generic parameter: T
296         //   3. The UpdateAndExecute methods don't do anything with the types,
297         //      we just want the CallSite args to be strongly typed to avoid
298         //      casting.
299         //   4. Works on desktop CLR with AppDomain that has only Execute
300         //      permission. In theory it might require RestrictedMemberAccess,
301         //      but it's unclear because we have tests passing without RMA.
302         //
303         // When Silverlight gets RMA we may be able to remove this.
304 #if SILVERLIGHT
305         [System.Security.SecuritySafeCritical]
306 #endif
307         private static Delegate CreateDelegateHelper(Type delegateType, MethodInfo method) {
308             return Delegate.CreateDelegate(delegateType, method);
309         }
310
311         private static bool IsSimpleSignature(MethodInfo invoke, out Type[] sig) {
312             ParameterInfo[] pis = invoke.GetParametersCached();
313             ContractUtils.Requires(pis.Length > 0 && pis[0].ParameterType == typeof(CallSite), "T");
314
315             Type[] args = new Type[invoke.ReturnType != typeof(void) ? pis.Length : pis.Length - 1];
316             bool supported = true;
317
318             for (int i = 1; i < pis.Length; i++) {
319                 ParameterInfo pi = pis[i];
320                 if (pi.IsByRefParameter()) {
321                     supported = false;
322                 }
323                 args[i - 1] = pi.ParameterType;
324             }
325             if (invoke.ReturnType != typeof(void)) {
326                 args[args.Length - 1] = invoke.ReturnType;
327             }
328             sig = args;
329             return supported;
330         }
331
332
333         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
334         private T CreateCustomNoMatchDelegate(MethodInfo invoke) {
335             var @params = invoke.GetParametersCached().Map(p => Expression.Parameter(p.ParameterType, p.Name));
336             return Expression.Lambda<T>(
337                 Expression.Block(
338                     Expression.Call(
339                         typeof(CallSiteOps).GetMethod("SetNotMatched"),
340                         @params.First()
341                     ),
342                     Expression.Default(invoke.GetReturnType())
343                 ),
344                 @params
345             ).Compile();
346         }
347
348         //
349         // WARNING: If you're changing this method, make sure you update the
350         // pregenerated versions as well, which are generated by
351         // generate_dynsites.py
352         // The two implementations *must* be kept functionally equivalent!
353         //
354         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
355         private T CreateCustomUpdateDelegate(MethodInfo invoke) {
356             var body = new List<Expression>();
357             var vars = new List<ParameterExpression>();
358             var @params = invoke.GetParametersCached().Map(p => Expression.Parameter(p.ParameterType, p.Name));
359             var @return = Expression.Label(invoke.GetReturnType());
360             var typeArgs = new[] { typeof(T) };
361
362             var site = @params[0];
363             var arguments = @params.RemoveFirst();
364
365             //var @this = (CallSite<T>)site;
366             var @this = Expression.Variable(typeof(CallSite<T>), "this");
367             vars.Add(@this);
368             body.Add(Expression.Assign(@this, Expression.Convert(site, @this.Type)));
369
370             //T[] applicable;
371             var applicable = Expression.Variable(typeof(T[]), "applicable");
372             vars.Add(applicable);
373
374             //T rule, originalRule = @this.Target;
375             var rule = Expression.Variable(typeof(T), "rule");
376             vars.Add(rule);
377
378             var originalRule = Expression.Variable(typeof(T), "originalRule");
379             vars.Add(originalRule);
380             body.Add(Expression.Assign(originalRule, Expression.Field(@this, "Target")));
381
382             //TRet result;
383             ParameterExpression result = null;
384             if (@return.Type != typeof(void)) {
385                 vars.Add(result = Expression.Variable(@return.Type, "result"));
386             }
387
388             //int count, index;
389             var count = Expression.Variable(typeof(int), "count");
390             vars.Add(count);
391             var index = Expression.Variable(typeof(int), "index");
392             vars.Add(index);
393
394             ////
395             //// Create matchmaker site. We'll need it regardless.
396             ////
397             //site = CallSiteOps.CreateMatchmaker();
398             body.Add(
399                 Expression.Assign(
400                     site,
401                     Expression.Call(
402                         typeof(CallSiteOps),
403                         "CreateMatchmaker",
404                         typeArgs, 
405                         @this
406                     )
407                 )
408             );
409
410             ////
411             //// Level 1 cache lookup
412             ////
413             //if ((applicable = CallSiteOps.GetRules(@this)) != null) {
414             //    for (index = 0, count = applicable.Length; index < count; index++) {
415             //        @this.Target = rule = applicable[i];
416
417             //        //
418             //        // Execute the rule
419             //        //
420             //
421             //        // if we've already tried it skip it...
422             //        if ((object)rule != (object)originalRule) {
423             //            %(setResult)s rule(site, %(args)s);
424             //            if (CallSiteOps.GetMatch(site)) {
425             //                CallSiteOps.UpdateRules(@this, i);
426             //                %(returnResult)s;
427             //            }
428             //
429             //            // Rule didn't match, try the next one
430             //            CallSiteOps.ClearMatch(site);
431             //        }
432             //    }
433             //}
434             Expression invokeRule;
435
436             Expression getMatch = Expression.Call(
437                 typeof(CallSiteOps).GetMethod("GetMatch"),
438                 site
439             );
440
441             Expression resetMatch = Expression.Call(
442                 typeof(CallSiteOps).GetMethod("ClearMatch"),
443                 site
444             );
445
446             var onMatch = Expression.Call(
447                 typeof(CallSiteOps),
448                 "UpdateRules",
449                 typeArgs,
450                 @this,
451                 index
452             );
453
454             if (@return.Type == typeof(void)) {
455                 invokeRule = Expression.Block(
456                     Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params)),
457                     Expression.IfThen(
458                         getMatch,
459                         Expression.Block(onMatch, Expression.Return(@return))
460                     )
461                 );
462             } else {
463                 invokeRule = Expression.Block(
464                     Expression.Assign(result, Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params))),
465                     Expression.IfThen(
466                         getMatch,
467                         Expression.Block(onMatch, Expression.Return(@return, result))
468                     )
469                 );
470             }
471
472             Expression getRule = Expression.Assign(rule, Expression.ArrayAccess(applicable, index));
473
474             var @break = Expression.Label();
475
476             var breakIfDone = Expression.IfThen(
477                 Expression.Equal(index, count),
478                 Expression.Break(@break)
479             );
480
481             var incrementIndex = Expression.PreIncrementAssign(index);
482
483             body.Add(
484                 Expression.IfThen(
485                     Expression.NotEqual(
486                         Expression.Assign(applicable, Expression.Call(typeof(CallSiteOps), "GetRules", typeArgs, @this)),
487                         Expression.Constant(null, applicable.Type)
488                     ),
489                     Expression.Block(
490                         Expression.Assign(count, Expression.ArrayLength(applicable)),
491                         Expression.Assign(index, Expression.Constant(0)),
492                         Expression.Loop(
493                             Expression.Block(
494                                 breakIfDone,
495                                 getRule,
496                                 Expression.IfThen(
497                                     Expression.NotEqual(
498                                         Expression.Convert(rule, typeof(object)),
499                                         Expression.Convert(originalRule, typeof(object))
500                                     ),
501                                     Expression.Block(
502                                         Expression.Assign(
503                                             Expression.Field(@this, "Target"),
504                                             rule
505                                         ),
506                                         invokeRule,
507                                         resetMatch
508                                     )
509                                 ),
510                                 incrementIndex
511                             ),
512                             @break,
513                             null
514                         )
515                     )
516                 )
517             );
518
519             ////
520             //// Level 2 cache lookup
521             ////
522             //
523             ////
524             //// Any applicable rules in level 2 cache?
525             ////
526             // 
527             // var cache = CallSiteOps.GetRuleCache(@this);
528
529             var cache = Expression.Variable(typeof(RuleCache<T>), "cache");
530             vars.Add(cache);
531
532             body.Add(
533                 Expression.Assign(
534                     cache,
535                     Expression.Call(typeof(CallSiteOps), "GetRuleCache", typeArgs, @this)
536                 )
537             );
538
539             // applicable = cache.GetRules();
540
541             body.Add(
542                 Expression.Assign(
543                     applicable,
544                     Expression.Call(typeof(CallSiteOps), "GetCachedRules", typeArgs, cache)
545                 )
546             );
547
548             //   for (int i = 0, count = applicable.Length; i < count; i++) {
549             //        @this.Target = rule = applicable[i];
550             //
551             //        //
552             //        // Execute the rule
553             //        //
554             //
555             //        try {
556             //            result = rule(site, arg0);
557             //            if (match) {
558             //                return result;
559             //            }
560             //        } finally {
561             //            if (CallSiteOps.GetMatch(site)) {
562             //                //
563             //                // Rule worked. Add it to level 1 cache
564             //                //
565             //
566             //                CallSiteOps.AddRule(@this, rule);
567             //                // and then move it to the front of the L2 cache
568             //                CallSiteOps.MoveRule(cache, rule, index);
569             //            }
570             //        }
571             //
572             //        // Rule didn't match, try the next one
573             //        CallSiteOps.ClearMatch(site);
574             //    }
575             //
576
577
578             // L2 invokeRule is different (no onMatch)
579             if (@return.Type == typeof(void)) {
580                 invokeRule = Expression.Block(
581                     Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params)),
582                     Expression.IfThen(
583                         getMatch,
584                         Expression.Return(@return)
585                     )
586                 );
587             } else {
588                 invokeRule = Expression.Block(
589                     Expression.Assign(result, Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params))),
590                     Expression.IfThen(
591                         getMatch,
592                         Expression.Return(@return, result)
593                     )
594                 );
595             }
596
597             var tryRule = Expression.TryFinally(
598                 invokeRule,
599                 Expression.IfThen(
600                     getMatch,
601                     Expression.Block(
602                         Expression.Call(typeof(CallSiteOps), "AddRule", typeArgs, @this, rule),
603                         Expression.Call(typeof(CallSiteOps), "MoveRule", typeArgs, cache, rule, index)
604                     )
605                 )
606             );
607
608             getRule = Expression.Assign(
609                 Expression.Field(@this, "Target"),
610                 Expression.Assign(rule, Expression.ArrayAccess(applicable, index))
611             );
612
613             body.Add(Expression.Assign(index, Expression.Constant(0)));
614             body.Add(Expression.Assign(count, Expression.ArrayLength(applicable)));
615             body.Add(
616                 Expression.Loop(
617                     Expression.Block(
618                         breakIfDone,
619                         getRule,
620                         tryRule,
621                         resetMatch,
622                         incrementIndex
623                     ),
624                     @break,
625                     null
626                 )
627             );
628
629             ////
630             //// Miss on Level 0, 1 and 2 caches. Create new rule
631             ////
632
633             //rule = null;
634             body.Add(Expression.Assign(rule, Expression.Constant(null, rule.Type)));
635
636             //var args = new object[] { arg0, arg1, ... };
637             var args = Expression.Variable(typeof(object[]), "args");
638             vars.Add(args);
639             body.Add(
640                 Expression.Assign(
641                     args,
642                     Expression.NewArrayInit(typeof(object), arguments.Map(p => Convert(p, typeof(object))))
643                 )
644             );
645
646             //for (; ; ) {
647             //    @this.Target = originalRule;
648             //    rule = @this.Target = @this.Binder.BindDelegate(@this, args);
649
650             //    //
651             //    // Execute the rule on the matchmaker site
652             //    //
653
654             //    try {
655             //        %(setResult)s ruleTarget(site, %(args)s);
656             //        if (match) {
657             //            %(returnResult)s;
658             //        }
659             //    } finally {
660             //        if (match) {
661             //            //
662             //            // The rule worked. Add it to level 1 cache.
663             //            //
664             //            CallSiteOps.AddRule(@this, rule);
665             //        }
666             //    }
667
668             //    // Rule we got back didn't work, try another one
669             //    match = true;
670             //}
671
672             Expression setOldTarget = Expression.Assign(
673                 Expression.Field(@this, "Target"),
674                 originalRule
675             );
676
677             getRule = Expression.Assign(
678                 Expression.Field(@this, "Target"),
679                 Expression.Assign(
680                     rule,
681                     Expression.Call(
682                         typeof(CallSiteOps),
683                         "Bind",
684                         typeArgs,
685                         Expression.Property(@this, "Binder"),
686                         @this,
687                         args
688                     )
689                 )
690             );
691
692             tryRule = Expression.TryFinally(
693                 invokeRule,
694                 Expression.IfThen(
695                     getMatch,
696                     Expression.Call(typeof(CallSiteOps), "AddRule", typeArgs, @this, rule)
697                 )
698             );
699
700             body.Add(
701                 Expression.Loop(
702                     Expression.Block(setOldTarget, getRule, tryRule, resetMatch),
703                     null, null
704                 )
705             );
706
707             body.Add(Expression.Default(@return.Type));
708
709             var lambda = Expression.Lambda<T>(
710                 Expression.Label(
711                     @return,
712                     Expression.Block(
713                         new ReadOnlyCollection<ParameterExpression>(vars),
714                         new ReadOnlyCollection<Expression>(body)
715                     )
716                 ),
717                 "CallSite.Target",
718                 true, // always compile the rules with tail call optimization
719                 new ReadOnlyCollection<ParameterExpression>(@params)
720             );
721
722             // Need to compile with forceDynamic because T could be invisible,
723             // or one of the argument types could be invisible
724             return lambda.Compile();
725         }
726
727         private static Expression Convert(Expression arg, Type type) {
728             if (TypeUtils.AreReferenceAssignable(type, arg.Type)) {
729                 return arg;
730             }
731             return Expression.Convert(arg, type);
732         }
733     }
734 }