PageRenderTime 45ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/Microsoft.Scripting.Core/Actions/CallSite.cs

https://bitbucket.org/stefanrusek/xronos
C# | 737 lines | 449 code | 93 blank | 195 comment | 38 complexity | 447b957aefdd84fbafd9edd70366d652 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System; using Microsoft;
  16. using System.Collections.Generic;
  17. using System.Collections.ObjectModel;
  18. using System.Diagnostics;
  19. #if CODEPLEX_40
  20. using System.Dynamic;
  21. using System.Dynamic.Utils;
  22. using System.Linq.Expressions;
  23. using System.Linq.Expressions.Compiler;
  24. #else
  25. using Microsoft.Scripting;
  26. using Microsoft.Scripting.Utils;
  27. using Microsoft.Linq.Expressions;
  28. using Microsoft.Linq.Expressions.Compiler;
  29. #endif
  30. using System.Reflection;
  31. using System.Threading;
  32. using System.Collections;
  33. #if CODEPLEX_40
  34. namespace System.Runtime.CompilerServices {
  35. #else
  36. namespace Microsoft.Runtime.CompilerServices {
  37. #endif
  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. /// <summary>
  58. /// A Dynamic Call Site base class. This type is used as a parameter type to the
  59. /// dynamic site targets. The first parameter of the delegate (T) below must be
  60. /// of this type.
  61. /// </summary>
  62. public class CallSite {
  63. // Cache of CallSite constructors for a given delegate type
  64. private static CacheDict<Type, Func<CallSiteBinder, CallSite>> _SiteCtors;
  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. // only CallSite<T> derives from this
  72. internal CallSite(CallSiteBinder binder) {
  73. _binder = binder;
  74. }
  75. /// <summary>
  76. /// used by Matchmaker sites to indicate rule match.
  77. /// </summary>
  78. internal bool _match;
  79. /// <summary>
  80. /// Class responsible for binding dynamic operations on the dynamic site.
  81. /// </summary>
  82. public CallSiteBinder Binder {
  83. get { return _binder; }
  84. }
  85. /// <summary>
  86. /// Creates a CallSite with the given delegate type and binder.
  87. /// </summary>
  88. /// <param name="delegateType">The CallSite delegate type.</param>
  89. /// <param name="binder">The CallSite binder.</param>
  90. /// <returns>The new CallSite.</returns>
  91. public static CallSite Create(Type delegateType, CallSiteBinder binder) {
  92. ContractUtils.RequiresNotNull(delegateType, "delegateType");
  93. ContractUtils.RequiresNotNull(binder, "binder");
  94. ContractUtils.Requires(delegateType.IsSubclassOf(typeof(Delegate)), "delegateType", Strings.TypeMustBeDerivedFromSystemDelegate);
  95. if (_SiteCtors == null) {
  96. // It's okay to just set this, worst case we're just throwing away some data
  97. _SiteCtors = new CacheDict<Type, Func<CallSiteBinder, CallSite>>(100);
  98. }
  99. Func<CallSiteBinder, CallSite> ctor;
  100. var ctors = _SiteCtors;
  101. lock (ctors) {
  102. if (!ctors.TryGetValue(delegateType, out ctor)) {
  103. MethodInfo method = typeof(CallSite<>).MakeGenericType(delegateType).GetMethod("Create");
  104. ctor = (Func<CallSiteBinder, CallSite>)Delegate.CreateDelegate(typeof(Func<CallSiteBinder, CallSite>), method);
  105. ctors.Add(delegateType, ctor);
  106. }
  107. }
  108. return ctor(binder);
  109. }
  110. }
  111. /// <summary>
  112. /// Dynamic site type.
  113. /// </summary>
  114. /// <typeparam name="T">The delegate type.</typeparam>
  115. public sealed partial class CallSite<T> : CallSite where T : class {
  116. /// <summary>
  117. /// The update delegate. Called when the dynamic site experiences cache miss.
  118. /// </summary>
  119. /// <returns>The update delegate.</returns>
  120. public T Update {
  121. get {
  122. // if this site is set up for match making, then use NoMatch as an Update
  123. if (_match) {
  124. Debug.Assert(_CachedNoMatch != null, "all normal sites should have Update cached once there is an instance.");
  125. return _CachedNoMatch;
  126. } else {
  127. Debug.Assert(_CachedUpdate != null, "all normal sites should have Update cached once there is an instance.");
  128. return _CachedUpdate;
  129. }
  130. }
  131. }
  132. /// <summary>
  133. /// The Level 0 cache - a delegate specialized based on the site history.
  134. /// </summary>
  135. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
  136. public T Target;
  137. /// <summary>
  138. /// The Level 1 cache - a history of the dynamic site.
  139. /// </summary>
  140. internal SmallRuleSet<T> Rules;
  141. // Cached update delegate for all sites with a given T
  142. private static T _CachedUpdate;
  143. // Cached noMatch delegate for all sites with a given T
  144. private static T _CachedNoMatch;
  145. private CallSite(CallSiteBinder binder)
  146. : base(binder) {
  147. Target = GetUpdateDelegate();
  148. }
  149. private CallSite()
  150. : base(null) {
  151. }
  152. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  153. internal CallSite<T> CreateMatchMaker() {
  154. return new CallSite<T>();
  155. }
  156. /// <summary>
  157. /// Creates an instance of the dynamic call site, initialized with the binder responsible for the
  158. /// runtime binding of the dynamic operations at this call site.
  159. /// </summary>
  160. /// <param name="binder">The binder responsible for the runtime binding of the dynamic operations at this call site.</param>
  161. /// <returns>The new instance of dynamic call site.</returns>
  162. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
  163. public static CallSite<T> Create(CallSiteBinder binder) {
  164. return new CallSite<T>(binder);
  165. }
  166. private T GetUpdateDelegate() {
  167. // This is intentionally non-static to speed up creation - in particular MakeUpdateDelegate
  168. // as static generic methods are more expensive than instance methods. We call a ref helper
  169. // so we only access the generic static field once.
  170. return GetUpdateDelegate(ref _CachedUpdate);
  171. }
  172. private T GetUpdateDelegate(ref T addr) {
  173. if (addr == null) {
  174. // reduce creation cost by not using Interlocked.CompareExchange. Calling I.CE causes
  175. // us to spend 25% of our creation time in JIT_GenericHandle. Instead we'll rarely
  176. // create 2 delegates with no other harm caused.
  177. addr = MakeUpdateDelegate();
  178. }
  179. return addr;
  180. }
  181. /// <summary>
  182. /// Clears the rule cache ... used by the call site tests.
  183. /// </summary>
  184. private void ClearRuleCache() {
  185. // make sure it initialized/atomized etc...
  186. Binder.GetRuleCache<T>();
  187. var cache = Binder.Cache;
  188. if (cache != null) {
  189. lock (cache) {
  190. cache.Clear();
  191. }
  192. }
  193. }
  194. internal T MakeUpdateDelegate() {
  195. Type target = typeof(T);
  196. Type[] args;
  197. MethodInfo invoke = target.GetMethod("Invoke");
  198. if (target.IsGenericType && IsSimpleSignature(invoke, out args)) {
  199. MethodInfo method = null;
  200. MethodInfo noMatchMethod = null;
  201. if (invoke.ReturnType == typeof(void)) {
  202. if (target == DelegateHelpers.GetActionType(args.AddFirst(typeof(CallSite)))) {
  203. method = typeof(UpdateDelegates).GetMethod("UpdateAndExecuteVoid" + args.Length, BindingFlags.NonPublic | BindingFlags.Static);
  204. noMatchMethod = typeof(UpdateDelegates).GetMethod("NoMatchVoid" + args.Length, BindingFlags.NonPublic | BindingFlags.Static);
  205. }
  206. } else {
  207. if (target == DelegateHelpers.GetFuncType(args.AddFirst(typeof(CallSite)))) {
  208. method = typeof(UpdateDelegates).GetMethod("UpdateAndExecute" + (args.Length - 1), BindingFlags.NonPublic | BindingFlags.Static);
  209. noMatchMethod = typeof(UpdateDelegates).GetMethod("NoMatch" + (args.Length - 1), BindingFlags.NonPublic | BindingFlags.Static);
  210. }
  211. }
  212. if (method != null) {
  213. _CachedNoMatch = (T)(object)noMatchMethod.MakeGenericMethod(args).CreateDelegate(target);
  214. return (T)(object)method.MakeGenericMethod(args).CreateDelegate(target);
  215. }
  216. }
  217. _CachedNoMatch = CreateCustomNoMatchDelegate(invoke);
  218. return CreateCustomUpdateDelegate(invoke);
  219. }
  220. private static bool IsSimpleSignature(MethodInfo invoke, out Type[] sig) {
  221. ParameterInfo[] pis = invoke.GetParametersCached();
  222. ContractUtils.Requires(pis.Length > 0 && pis[0].ParameterType == typeof(CallSite), "T");
  223. Type[] args = new Type[invoke.ReturnType != typeof(void) ? pis.Length : pis.Length - 1];
  224. bool supported = true;
  225. for (int i = 1; i < pis.Length; i++) {
  226. ParameterInfo pi = pis[i];
  227. if (pi.IsByRefParameter()) {
  228. supported = false;
  229. }
  230. args[i - 1] = pi.ParameterType;
  231. }
  232. if (invoke.ReturnType != typeof(void)) {
  233. args[args.Length - 1] = invoke.ReturnType;
  234. }
  235. sig = args;
  236. return supported;
  237. }
  238. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  239. private T CreateCustomNoMatchDelegate(MethodInfo invoke) {
  240. var @params = invoke.GetParametersCached().Map(p => Expression.Parameter(p.ParameterType, p.Name));
  241. var site = @params[0];
  242. return Expression.Lambda<T>(
  243. Expression.Block(
  244. Expression.Call(
  245. typeof(CallSiteOps).GetMethod("SetNotMatched"),
  246. @params.First()
  247. ),
  248. Expression.Default(invoke.GetReturnType())
  249. ),
  250. @params
  251. ).Compile();
  252. }
  253. //
  254. // WARNING: If you're changing this method, make sure you update the
  255. // pregenerated versions as well, which are generated by
  256. // generate_dynsites.py
  257. // The two implementations *must* be kept functionally equivalent!
  258. //
  259. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  260. private T CreateCustomUpdateDelegate(MethodInfo invoke) {
  261. var body = new List<Expression>();
  262. var vars = new List<ParameterExpression>();
  263. var @params = invoke.GetParametersCached().Map(p => Expression.Parameter(p.ParameterType, p.Name));
  264. var @return = Expression.Label(invoke.GetReturnType());
  265. var typeArgs = new[] { typeof(T) };
  266. var site = @params[0];
  267. var arguments = @params.RemoveFirst();
  268. //var @this = (CallSite<T>)site;
  269. var @this = Expression.Variable(typeof(CallSite<T>), "this");
  270. vars.Add(@this);
  271. body.Add(Expression.Assign(@this, Expression.Convert(site, @this.Type)));
  272. //T[] applicable;
  273. var applicable = Expression.Variable(typeof(T[]), "applicable");
  274. vars.Add(applicable);
  275. //T rule, originalRule = @this.Target;
  276. var rule = Expression.Variable(typeof(T), "rule");
  277. vars.Add(rule);
  278. var originalRule = Expression.Variable(typeof(T), "originalRule");
  279. vars.Add(originalRule);
  280. //TRet result;
  281. ParameterExpression result = null;
  282. if (@return.Type != typeof(void)) {
  283. vars.Add(result = Expression.Variable(@return.Type, "result"));
  284. }
  285. //int count, index;
  286. var count = Expression.Variable(typeof(int), "count");
  287. vars.Add(count);
  288. var index = Expression.Variable(typeof(int), "index");
  289. vars.Add(index);
  290. ////
  291. //// Create matchmaker site. We'll need it regardless.
  292. ////
  293. //site = CallSiteOps.CreateMatchmaker();
  294. body.Add(
  295. Expression.Assign(
  296. site,
  297. Expression.Call(
  298. typeof(CallSiteOps),
  299. "CreateMatchmaker",
  300. typeArgs,
  301. @this
  302. )
  303. )
  304. );
  305. ////
  306. //// Level 1 cache lookup
  307. ////
  308. //if ((applicable = CallSiteOps.GetRules(@this)) != null) {
  309. // for (index = 0, count = applicable.Length; index < count; index++) {
  310. // @this.Target = rule = applicable[i];
  311. // //
  312. // // Execute the rule
  313. // //
  314. //
  315. // // if we've already tried it skip it...
  316. // if ((object)rule != (object)originalRule) {
  317. // %(setResult)s rule(site, %(args)s);
  318. // if (CallSiteOps.GetMatch(site)) {
  319. // CallSiteOps.UpdateRules(@this, i);
  320. // %(returnResult)s;
  321. // }
  322. //
  323. // // Rule didn't match, try the next one
  324. // CallSiteOps.ClearMatch(site);
  325. // }
  326. // }
  327. //}
  328. Expression invokeRule;
  329. Expression getMatch = Expression.Call(
  330. typeof(CallSiteOps).GetMethod("GetMatch"),
  331. site
  332. );
  333. Expression resetMatch = Expression.Call(
  334. typeof(CallSiteOps).GetMethod("ClearMatch"),
  335. site
  336. );
  337. var onMatch = Expression.Call(
  338. typeof(CallSiteOps),
  339. "UpdateRules",
  340. typeArgs,
  341. @this,
  342. index
  343. );
  344. if (@return.Type == typeof(void)) {
  345. invokeRule = Expression.Block(
  346. Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params)),
  347. Expression.IfThen(
  348. getMatch,
  349. Expression.Block(onMatch, Expression.Return(@return))
  350. )
  351. );
  352. } else {
  353. invokeRule = Expression.Block(
  354. Expression.Assign(result, Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params))),
  355. Expression.IfThen(
  356. getMatch,
  357. Expression.Block(onMatch, Expression.Return(@return, result))
  358. )
  359. );
  360. }
  361. Expression getRule = Expression.Assign(
  362. rule,
  363. Expression.ArrayAccess(applicable, index)
  364. );
  365. var @break = Expression.Label();
  366. var breakIfDone = Expression.IfThen(
  367. Expression.Equal(index, count),
  368. Expression.Break(@break)
  369. );
  370. var incrementIndex = Expression.PreIncrementAssign(index);
  371. body.Add(
  372. Expression.IfThen(
  373. Expression.NotEqual(
  374. Expression.Assign(applicable, Expression.Call(typeof(CallSiteOps), "GetRules", typeArgs, @this)),
  375. Expression.Constant(null, applicable.Type)
  376. ),
  377. Expression.Block(
  378. Expression.Assign(count, Expression.ArrayLength(applicable)),
  379. Expression.Assign(index, Expression.Constant(0)),
  380. Expression.Loop(
  381. Expression.Block(
  382. breakIfDone,
  383. getRule,
  384. Expression.IfThen(
  385. Expression.NotEqual(
  386. Expression.Convert(rule, typeof(object)),
  387. Expression.Convert(originalRule, typeof(object))
  388. ),
  389. Expression.Block(
  390. Expression.Assign(
  391. Expression.Field(
  392. @this,
  393. "Target"
  394. ),
  395. rule
  396. ),
  397. invokeRule,
  398. resetMatch
  399. )
  400. ),
  401. incrementIndex
  402. ),
  403. @break,
  404. null
  405. )
  406. )
  407. )
  408. );
  409. ////
  410. //// Level 2 cache lookup
  411. ////
  412. // i = 0;
  413. // var cache = @this.Binder.GetRuleCache<%(funcType)s>();
  414. var cache = Expression.Variable(typeof(RuleCache<T>), "cache");
  415. vars.Add(cache);
  416. body.Add(
  417. Expression.Assign(
  418. cache,
  419. Expression.Call(typeof(CallSiteOps), "GetRuleCache", typeArgs, @this)
  420. )
  421. );
  422. body.Add(
  423. Expression.Assign(
  424. index,
  425. Expression.Constant(0)
  426. )
  427. );
  428. ////
  429. //// Any applicable rules in level 2 cache?
  430. ////
  431. // foreach (var cachedRule in CallSiteOps.GetCachedRules<%(funcType)s>(cache)) {
  432. ParameterExpression enumerator = Expression.Parameter(typeof(IEnumerator<>).MakeGenericType(typeArgs), "enum");
  433. vars.Add(enumerator);
  434. body.Add(
  435. Expression.Assign(
  436. enumerator,
  437. Expression.Call(
  438. Expression.Call(
  439. typeof(CallSiteOps),
  440. "GetCachedRules",
  441. typeArgs,
  442. cache
  443. ),
  444. typeof(IEnumerable<>).MakeGenericType(typeArgs).GetMethod("GetEnumerator")
  445. )
  446. )
  447. );
  448. // @this.Target = rule;
  449. //
  450. // //
  451. // // Execute the rule
  452. // //
  453. //
  454. // try {
  455. // result = rule(site, arg0);
  456. // if (match) {
  457. // return result;
  458. // }
  459. // } finally {
  460. // if (CallSiteOps.GetMatch(site)) {
  461. // //
  462. // // Rule worked. Add it to level 1 cache
  463. // //
  464. //
  465. // CallSiteOps.AddRule(@this, rule);
  466. // // and then move it to the front of the L2 cache
  467. // CallSiteOps.MoveRule(cache, rule, index);
  468. // }
  469. // }
  470. //
  471. // // Rule didn't match, try the next one
  472. // CallSiteOps.ClearMatch(site);
  473. // }
  474. //
  475. // L2 invokeRule is different (no onMatch)
  476. if (@return.Type == typeof(void)) {
  477. invokeRule = Expression.Block(
  478. Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params)),
  479. Expression.IfThen(
  480. getMatch,
  481. Expression.Return(@return)
  482. )
  483. );
  484. } else {
  485. invokeRule = Expression.Block(
  486. Expression.Assign(result, Expression.Invoke(rule, new TrueReadOnlyCollection<Expression>(@params))),
  487. Expression.IfThen(
  488. getMatch,
  489. Expression.Return(@return, result)
  490. )
  491. );
  492. }
  493. var tryRule = Expression.TryFinally(
  494. invokeRule,
  495. Expression.IfThen(
  496. getMatch,
  497. Expression.Block(
  498. Expression.Call(typeof(CallSiteOps), "AddRule", typeArgs, @this, rule),
  499. Expression.Call(typeof(CallSiteOps), "MoveRule", typeArgs, cache, rule, index)
  500. )
  501. )
  502. );
  503. body.Add(
  504. Expression.Assign(index, Expression.Constant(0))
  505. );
  506. breakIfDone = Expression.IfThen(
  507. Expression.Not(
  508. Expression.Call(
  509. enumerator,
  510. typeof(IEnumerator).GetMethod("MoveNext")
  511. )
  512. ),
  513. Expression.Break(@break)
  514. );
  515. getRule = Expression.Assign(
  516. rule,
  517. Expression.Assign(
  518. Expression.Field(
  519. @this,
  520. "Target"
  521. ),
  522. Expression.Property(
  523. enumerator,
  524. "Current"
  525. )
  526. )
  527. );
  528. body.Add(
  529. Expression.Loop(
  530. Expression.Block(
  531. breakIfDone,
  532. getRule,
  533. tryRule,
  534. resetMatch,
  535. incrementIndex
  536. ),
  537. @break,
  538. null
  539. )
  540. );
  541. ////
  542. //// Miss on Level 0, 1 and 2 caches. Create new rule
  543. ////
  544. //rule = null;
  545. body.Add(Expression.Assign(rule, Expression.Constant(null, rule.Type)));
  546. //var args = new object[] { arg0, arg1, ... };
  547. var args = Expression.Variable(typeof(object[]), "args");
  548. vars.Add(args);
  549. body.Add(
  550. Expression.Assign(
  551. args,
  552. Expression.NewArrayInit(typeof(object), arguments.Map(p => Convert(p, typeof(object))))
  553. )
  554. );
  555. //for (; ; ) {
  556. // @this.Target = originalRule;
  557. // rule = @this.Target = @this.Binder.BindDelegate(@this, args);
  558. // //
  559. // // Execute the rule on the matchmaker site
  560. // //
  561. // try {
  562. // %(setResult)s ruleTarget(site, %(args)s);
  563. // if (match) {
  564. // %(returnResult)s;
  565. // }
  566. // } finally {
  567. // if (match) {
  568. // //
  569. // // The rule worked. Add it to level 1 cache.
  570. // //
  571. // CallSiteOps.AddRule(@this, rule);
  572. // }
  573. // }
  574. // // Rule we got back didn't work, try another one
  575. // match = true;
  576. //}
  577. Expression setOldTarget = Expression.Assign(
  578. Expression.Field(
  579. @this,
  580. "Target"
  581. ),
  582. originalRule
  583. );
  584. getRule = Expression.Assign(
  585. Expression.Field(
  586. @this,
  587. "Target"
  588. ),
  589. Expression.Assign(
  590. rule,
  591. Expression.Call(
  592. Expression.Property(
  593. @this,
  594. "Binder"
  595. ),
  596. "BindDelegate",
  597. typeArgs,
  598. @this,
  599. args
  600. )
  601. )
  602. );
  603. tryRule = Expression.TryFinally(
  604. invokeRule,
  605. Expression.IfThen(
  606. getMatch,
  607. Expression.Call(typeof(CallSiteOps), "AddRule", typeArgs, @this, rule)
  608. )
  609. );
  610. body.Add(
  611. Expression.Loop(
  612. Expression.Block(setOldTarget, getRule, tryRule, resetMatch),
  613. null, null
  614. )
  615. );
  616. body.Add(Expression.Default(@return.Type));
  617. var lambda = Expression.Lambda<T>(
  618. Expression.Label(
  619. @return,
  620. Expression.Block(
  621. new ReadOnlyCollection<ParameterExpression>(vars),
  622. new ReadOnlyCollection<Expression>(body)
  623. )
  624. ),
  625. "CallSite.Target",
  626. true, // always compile the rules with tail call optimization
  627. new ReadOnlyCollection<ParameterExpression>(@params)
  628. );
  629. // Need to compile with forceDynamic because T could be invisible,
  630. // or one of the argument types could be invisible
  631. return lambda.Compile();
  632. }
  633. private static Expression Convert(Expression arg, Type type) {
  634. if (TypeUtils.AreReferenceAssignable(type, arg.Type)) {
  635. return arg;
  636. }
  637. return Expression.Convert(arg, type);
  638. }
  639. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  640. internal CallSiteRule<T> MakeRule(T target) {
  641. return new CallSiteRule<T>(null, target);
  642. }
  643. }
  644. }