PageRenderTime 30ms CodeModel.GetById 3ms RepoModel.GetById 0ms app.codeStats 1ms

/Microsoft.Scripting/Actions/Calls/MethodTarget.cs

https://bitbucket.org/stefanrusek/xronos
C# | 520 lines | 393 code | 74 blank | 53 comment | 121 complexity | 5b20543b983f99c75ab030d62a6baf9f 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. #if CODEPLEX_40
  16. using System;
  17. #else
  18. using System; using Microsoft;
  19. #endif
  20. using System.Collections.Generic;
  21. using System.Diagnostics;
  22. #if CODEPLEX_40
  23. using System.Linq.Expressions;
  24. #else
  25. using Microsoft.Linq.Expressions;
  26. #endif
  27. using System.Reflection;
  28. using System.Text;
  29. using Microsoft.Contracts;
  30. using Microsoft.Scripting.Generation;
  31. using Microsoft.Scripting.Runtime;
  32. using Microsoft.Scripting.Utils;
  33. using AstUtils = Microsoft.Scripting.Ast.Utils;
  34. namespace Microsoft.Scripting.Actions.Calls {
  35. #if CODEPLEX_40
  36. using Ast = System.Linq.Expressions.Expression;
  37. #else
  38. using Ast = Microsoft.Linq.Expressions.Expression;
  39. #endif
  40. /// <summary>
  41. /// MethodTarget represents how a method is bound to the arguments of the call-site
  42. ///
  43. /// Contrast this with MethodCandidate which represents the logical view of the invocation of a method
  44. /// </summary>
  45. public sealed class MethodTarget {
  46. private MethodBinder _binder;
  47. private readonly MethodBase _method;
  48. private int _parameterCount;
  49. private IList<ArgBuilder> _argBuilders;
  50. private ArgBuilder _instanceBuilder;
  51. private ReturnBuilder _returnBuilder;
  52. internal MethodTarget(MethodBinder binder, MethodBase method, int parameterCount, ArgBuilder instanceBuilder, IList<ArgBuilder> argBuilders, ReturnBuilder returnBuilder) {
  53. this._binder = binder;
  54. this._method = method;
  55. this._parameterCount = parameterCount;
  56. this._instanceBuilder = instanceBuilder;
  57. this._argBuilders = argBuilders;
  58. this._returnBuilder = returnBuilder;
  59. //argBuilders.TrimExcess();
  60. }
  61. public MethodBinder Binder {
  62. get { return _binder; }
  63. }
  64. public MethodBase Method {
  65. get { return _method; }
  66. }
  67. public int ParameterCount {
  68. get { return _parameterCount; }
  69. }
  70. public Type ReturnType {
  71. get {
  72. return _returnBuilder.ReturnType;
  73. }
  74. }
  75. public Type[] GetParameterTypes() {
  76. List<Type> res = new List<Type>(_argBuilders.Count);
  77. for (int i = 0; i < _argBuilders.Count; i++) {
  78. Type t = _argBuilders[i].Type;
  79. if (t != null) {
  80. res.Add(t);
  81. }
  82. }
  83. return res.ToArray();
  84. }
  85. public string GetSignatureString(CallTypes callType) {
  86. StringBuilder buf = new StringBuilder();
  87. Type[] types = GetParameterTypes();
  88. if (callType == CallTypes.ImplicitInstance) {
  89. types = ArrayUtils.RemoveFirst(types);
  90. }
  91. string comma = "";
  92. buf.Append("(");
  93. foreach (Type t in types) {
  94. buf.Append(comma);
  95. buf.Append(t.Name);
  96. comma = ", ";
  97. }
  98. buf.Append(")");
  99. return buf.ToString();
  100. }
  101. [Confined]
  102. public override string ToString() {
  103. return string.Format("MethodTarget({0} on {1})", Method, Method.DeclaringType.FullName);
  104. }
  105. internal OptimizingCallDelegate MakeDelegate(ParameterBinder parameterBinder, RestrictionInfo restrictionInfo) {
  106. MethodInfo mi = Method as MethodInfo;
  107. if (mi == null) {
  108. return null;
  109. }
  110. Type declType = mi.GetBaseDefinition().DeclaringType;
  111. if (declType != null &&
  112. declType.Assembly == typeof(string).Assembly &&
  113. declType.IsSubclassOf(typeof(MemberInfo))) {
  114. // members of reflection are off limits via reflection in partial trust
  115. return null;
  116. }
  117. if (_returnBuilder.CountOutParams > 0) {
  118. return null;
  119. }
  120. // if we have a non-visible method see if we can find a better method which
  121. // will call the same thing but is visible. If this fails we still bind anyway - it's
  122. // the callers responsibility to filter out non-visible methods.
  123. mi = CompilerHelpers.TryGetCallableMethod(mi);
  124. Func<object[], object>[] builders = new Func<object[],object>[_argBuilders.Count];
  125. bool[] hasBeenUsed = new bool[restrictionInfo.Objects.Length];
  126. for (int i = 0; i < _argBuilders.Count; i++) {
  127. if (!_argBuilders[i].CanGenerateDelegate) {
  128. return null;
  129. }
  130. builders[i] = _argBuilders[i].ToDelegate(parameterBinder, restrictionInfo.Objects, hasBeenUsed);
  131. }
  132. if (_instanceBuilder != null && !(_instanceBuilder is NullArgBuilder)) {
  133. return new Caller(mi, builders, _instanceBuilder.ToDelegate(parameterBinder, restrictionInfo.Objects, hasBeenUsed)).CallWithInstance;
  134. } else {
  135. return new Caller(mi, builders, null).Call;
  136. }
  137. }
  138. class Caller {
  139. private readonly Func<object[], object>[] _argBuilders;
  140. private readonly Func<object[], object> _instanceBuilder;
  141. private readonly MethodInfo _mi;
  142. private ReflectedCaller _caller;
  143. private int _hitCount;
  144. public Caller(MethodInfo mi, Func<object[], object>[] argBuilders, Func<object[], object> instanceBuilder) {
  145. _mi = mi;
  146. _argBuilders = argBuilders;
  147. _instanceBuilder = instanceBuilder;
  148. }
  149. public object Call(object[] args, out bool shouldOptimize) {
  150. shouldOptimize = TrackUsage(args);
  151. try {
  152. if (_caller != null) {
  153. return _caller.Invoke(GetArguments(args));
  154. }
  155. return _mi.Invoke(null, GetArguments(args));
  156. } catch (TargetInvocationException tie) {
  157. ExceptionHelpers.UpdateForRethrow(tie.InnerException);
  158. throw tie.InnerException;
  159. }
  160. }
  161. public object CallWithInstance(object[] args, out bool shouldOptimize) {
  162. shouldOptimize = TrackUsage(args);
  163. try {
  164. if (_caller != null) {
  165. return _caller.InvokeInstance(_instanceBuilder(args), GetArguments(args));
  166. }
  167. return _mi.Invoke(_instanceBuilder(args), GetArguments(args));
  168. } catch (TargetInvocationException tie) {
  169. ExceptionHelpers.UpdateForRethrow(tie.InnerException);
  170. throw tie.InnerException;
  171. }
  172. }
  173. private object[] GetArguments(object[] args) {
  174. object[] finalArgs = new object[_argBuilders.Length];
  175. for (int i = 0; i < finalArgs.Length; i++) {
  176. finalArgs[i] = _argBuilders[i](args);
  177. }
  178. return finalArgs;
  179. }
  180. private bool TrackUsage(object[] args) {
  181. bool shouldOptimize;
  182. _hitCount++;
  183. shouldOptimize = false;
  184. bool forceCaller = false;
  185. if (_hitCount <= 100 && _caller == null) {
  186. foreach (object o in args) {
  187. // can't pass Missing.Value via reflection, use a ReflectedCaller
  188. if (o == Missing.Value) {
  189. forceCaller = true;
  190. }
  191. }
  192. }
  193. if (_hitCount > 100) {
  194. shouldOptimize = true;
  195. } else if ((_hitCount > 5 || forceCaller) && _caller == null) {
  196. _caller = ReflectedCaller.Create(_mi);
  197. }
  198. return shouldOptimize;
  199. }
  200. }
  201. internal Expression MakeExpression(ParameterBinder parameterBinder, IList<Expression> parameters) {
  202. bool[] usageMarkers;
  203. Expression[] spilledArgs;
  204. Expression[] args = GetArgumentExpressions(parameterBinder, parameters, out usageMarkers, out spilledArgs);
  205. MethodBase mb = Method;
  206. MethodInfo mi = mb as MethodInfo;
  207. Expression ret, call;
  208. if (mi != null) {
  209. // if we have a non-visible method see if we can find a better method which
  210. // will call the same thing but is visible. If this fails we still bind anyway - it's
  211. // the callers responsibility to filter out non-visible methods.
  212. mb = CompilerHelpers.TryGetCallableMethod(mi);
  213. }
  214. ConstructorInfo ci = mb as ConstructorInfo;
  215. Debug.Assert(mi != null || ci != null);
  216. if (CompilerHelpers.IsVisible(mb)) {
  217. // public method
  218. if (mi != null) {
  219. Expression instance = mi.IsStatic ? null : _instanceBuilder.ToExpression(parameterBinder, parameters, usageMarkers);
  220. call = AstUtils.SimpleCallHelper(instance, mi, args);
  221. } else {
  222. call = AstUtils.SimpleNewHelper(ci, args);
  223. }
  224. } else {
  225. // Private binding, invoke via reflection
  226. if (mi != null) {
  227. Expression instance = mi.IsStatic ? AstUtils.Constant(null) : _instanceBuilder.ToExpression(parameterBinder, parameters, usageMarkers);
  228. Debug.Assert(instance != null, "Can't skip instance expression");
  229. call = Ast.Call(
  230. typeof(BinderOps).GetMethod("InvokeMethod"),
  231. AstUtils.Constant(mi),
  232. AstUtils.Convert(instance, typeof(object)),
  233. AstUtils.NewArrayHelper(typeof(object), args)
  234. );
  235. } else {
  236. call = Ast.Call(
  237. typeof(BinderOps).GetMethod("InvokeConstructor"),
  238. AstUtils.Constant(ci),
  239. AstUtils.NewArrayHelper(typeof(object), args)
  240. );
  241. }
  242. }
  243. if (spilledArgs != null) {
  244. call = Expression.Block(spilledArgs.AddLast(call));
  245. }
  246. ret = _returnBuilder.ToExpression(parameterBinder, _argBuilders, parameters, call);
  247. List<Expression> updates = null;
  248. for (int i = 0; i < _argBuilders.Count; i++) {
  249. Expression next = _argBuilders[i].UpdateFromReturn(parameterBinder, parameters);
  250. if (next != null) {
  251. if (updates == null) {
  252. updates = new List<Expression>();
  253. }
  254. updates.Add(next);
  255. }
  256. }
  257. if (updates != null) {
  258. if (ret.Type != typeof(void)) {
  259. ParameterExpression temp = Ast.Variable(ret.Type, "$ret");
  260. updates.Insert(0, Ast.Assign(temp, ret));
  261. updates.Add(temp);
  262. ret = Ast.Block(new [] { temp }, updates.ToArray());
  263. } else {
  264. updates.Insert(0, ret);
  265. ret = Ast.Block(typeof(void), updates.ToArray());
  266. }
  267. }
  268. if (parameterBinder.Temps != null) {
  269. ret = Ast.Block(parameterBinder.Temps, ret);
  270. }
  271. return ret;
  272. }
  273. private Expression[] GetArgumentExpressions(ParameterBinder parameterBinder, IList<Expression> parameters, out bool[] usageMarkers, out Expression[] spilledArgs) {
  274. int minPriority = Int32.MaxValue;
  275. int maxPriority = Int32.MinValue;
  276. foreach (ArgBuilder ab in _argBuilders) {
  277. minPriority = System.Math.Min(minPriority, ab.Priority);
  278. maxPriority = System.Math.Max(maxPriority, ab.Priority);
  279. }
  280. var args = new Expression[_argBuilders.Count];
  281. Expression[] actualArgs = null;
  282. usageMarkers = new bool[parameters.Count];
  283. for (int priority = minPriority; priority <= maxPriority; priority++) {
  284. for (int i = 0; i < _argBuilders.Count; i++) {
  285. if (_argBuilders[i].Priority == priority) {
  286. args[i] = _argBuilders[i].ToExpression(parameterBinder, parameters, usageMarkers);
  287. // see if this has a temp that needs to be passed as the actual argument
  288. Expression byref = _argBuilders[i].ByRefArgument;
  289. if (byref != null) {
  290. if (actualArgs == null) {
  291. actualArgs = new Expression[_argBuilders.Count];
  292. }
  293. actualArgs[i] = byref;
  294. }
  295. }
  296. }
  297. }
  298. if (actualArgs != null) {
  299. for (int i = 0; i < args.Length; i++) {
  300. if (args[i] != null && actualArgs[i] == null) {
  301. actualArgs[i] = parameterBinder.GetTemporary(args[i].Type, null);
  302. args[i] = Expression.Assign(actualArgs[i], args[i]);
  303. }
  304. }
  305. spilledArgs = RemoveNulls(args);
  306. return RemoveNulls(actualArgs);
  307. }
  308. spilledArgs = null;
  309. return RemoveNulls(args);
  310. }
  311. private static Expression[] RemoveNulls(Expression[] args) {
  312. int newLength = args.Length;
  313. for (int i = 0; i < args.Length; i++) {
  314. if (args[i] == null) {
  315. newLength--;
  316. }
  317. }
  318. var result = new Expression[newLength];
  319. for (int i = 0, j = 0; i < args.Length; i++) {
  320. if (args[i] != null) {
  321. result[j++] = args[i];
  322. }
  323. }
  324. return result;
  325. }
  326. /// <summary>
  327. /// Creates a call to this MethodTarget with the specified parameters. Casts are inserted to force
  328. /// the types to the provided known types.
  329. ///
  330. /// TODO: Remove RuleBuilder and knownTypes once we're fully meta
  331. /// </summary>
  332. /// <param name="parameterBinder">ParameterBinder used to map arguments to parameters.</param>
  333. /// <param name="parameters">The explicit arguments</param>
  334. /// <param name="knownTypes">If non-null, the type for each element in parameters</param>
  335. /// <returns></returns>
  336. internal Expression MakeExpression(ParameterBinder parameterBinder, IList<Expression> parameters, IList<Type> knownTypes) {
  337. Debug.Assert(knownTypes == null || parameters.Count == knownTypes.Count);
  338. IList<Expression> args = parameters;
  339. if (knownTypes != null) {
  340. args = new Expression[parameters.Count];
  341. for (int i = 0; i < args.Count; i++) {
  342. args[i] = parameters[i];
  343. if (knownTypes[i] != null && !knownTypes[i].IsAssignableFrom(parameters[i].Type)) {
  344. args[i] = AstUtils.Convert(parameters[i], CompilerHelpers.GetVisibleType(knownTypes[i]));
  345. }
  346. }
  347. }
  348. return MakeExpression(parameterBinder, args);
  349. }
  350. private static int FindMaxPriority(IList<ArgBuilder> abs, int ceiling) {
  351. int max = 0;
  352. foreach (ArgBuilder ab in abs) {
  353. if (ab.Priority > ceiling) continue;
  354. max = System.Math.Max(max, ab.Priority);
  355. }
  356. return max;
  357. }
  358. internal static Candidate CompareEquivalentParameters(MethodTarget one, MethodTarget two) {
  359. // Prefer normal methods over explicit interface implementations
  360. if (two.Method.IsPrivate && !one.Method.IsPrivate) return Candidate.One;
  361. if (one.Method.IsPrivate && !two.Method.IsPrivate) return Candidate.Two;
  362. // Prefer non-generic methods over generic methods
  363. if (one.Method.IsGenericMethod) {
  364. if (!two.Method.IsGenericMethod) {
  365. return Candidate.Two;
  366. } else {
  367. //!!! Need to support selecting least generic method here
  368. return Candidate.Equivalent;
  369. }
  370. } else if (two.Method.IsGenericMethod) {
  371. return Candidate.One;
  372. }
  373. //prefer methods without out params over those with them
  374. switch (Compare(one._returnBuilder.CountOutParams, two._returnBuilder.CountOutParams)) {
  375. case 1: return Candidate.Two;
  376. case -1: return Candidate.One;
  377. }
  378. //prefer methods using earlier conversions rules to later ones
  379. for (int i = Int32.MaxValue; i >= 0; ) {
  380. int maxPriorityThis = FindMaxPriority(one._argBuilders, i);
  381. int maxPriorityOther = FindMaxPriority(two._argBuilders, i);
  382. if (maxPriorityThis < maxPriorityOther) return Candidate.One;
  383. if (maxPriorityOther < maxPriorityThis) return Candidate.Two;
  384. i = maxPriorityThis - 1;
  385. }
  386. return Candidate.Equivalent;
  387. }
  388. private static int Compare(int x, int y) {
  389. if (x < y) return -1;
  390. else if (x > y) return +1;
  391. else return 0;
  392. }
  393. internal MethodTarget MakeParamsExtended(int argCount, string[] names, int[] nameIndexes) {
  394. Debug.Assert(BinderHelpers.IsParamsMethod(Method));
  395. List<ArgBuilder> newArgBuilders = new List<ArgBuilder>(_argBuilders.Count);
  396. // current argument that we consume, initially skip this if we have it.
  397. int curArg = CompilerHelpers.IsStatic(_method) ? 0 : 1;
  398. int kwIndex = -1;
  399. ArgBuilder paramsDictBuilder = null;
  400. foreach (ArgBuilder ab in _argBuilders) {
  401. SimpleArgBuilder sab = ab as SimpleArgBuilder;
  402. if (sab != null) {
  403. // we consume one or more incoming argument(s)
  404. if (sab.IsParamsArray) {
  405. // consume all the extra arguments
  406. int paramsUsed = argCount -
  407. GetConsumedArguments() -
  408. names.Length +
  409. (CompilerHelpers.IsStatic(_method) ? 1 : 0);
  410. newArgBuilders.Add(new ParamsArgBuilder(
  411. sab.ParameterInfo,
  412. sab.Type.GetElementType(),
  413. curArg,
  414. paramsUsed
  415. ));
  416. curArg += paramsUsed;
  417. } else if (sab.IsParamsDict) {
  418. // consume all the kw arguments
  419. kwIndex = newArgBuilders.Count;
  420. paramsDictBuilder = sab;
  421. } else {
  422. // consume the argument, adjust its position:
  423. newArgBuilders.Add(sab.MakeCopy(curArg++));
  424. }
  425. } else {
  426. // CodeContext, null, default, etc... we don't consume an
  427. // actual incoming argument.
  428. newArgBuilders.Add(ab);
  429. }
  430. }
  431. if (kwIndex != -1) {
  432. newArgBuilders.Insert(kwIndex, new ParamsDictArgBuilder(paramsDictBuilder.ParameterInfo, curArg, names, nameIndexes));
  433. }
  434. return new MethodTarget(_binder, Method, argCount, _instanceBuilder, newArgBuilders, _returnBuilder);
  435. }
  436. private int GetConsumedArguments() {
  437. int consuming = 0;
  438. foreach (ArgBuilder argb in _argBuilders) {
  439. SimpleArgBuilder sab = argb as SimpleArgBuilder;
  440. if (sab != null && !sab.IsParamsDict) consuming++;
  441. }
  442. return consuming;
  443. }
  444. }
  445. public delegate object OptimizingCallDelegate(object[]args, out bool shouldOptimize);
  446. }