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