PageRenderTime 57ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/Binding/MetaPythonFunction.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 1141 lines | 837 code | 156 blank | 148 comment | 136 complexity | 9490ddca627212f5547d76ad93a078a9 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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 !CLR2
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. using System.Diagnostics;
  24. using System.Reflection;
  25. using System.Dynamic;
  26. using Microsoft.Scripting;
  27. using Microsoft.Scripting.Actions;
  28. using Microsoft.Scripting.Actions.Calls;
  29. using Microsoft.Scripting.Runtime;
  30. using Microsoft.Scripting.Utils;
  31. using IronPython.Runtime.Operations;
  32. using IronPython.Runtime.Types;
  33. namespace IronPython.Runtime.Binding {
  34. using Ast = Expression;
  35. using AstUtils = Microsoft.Scripting.Ast.Utils;
  36. class MetaPythonFunction : MetaPythonObject, IPythonInvokable, IPythonOperable, IPythonConvertible, IInferableInvokable, IConvertibleMetaObject, IPythonGetable {
  37. public MetaPythonFunction(Expression/*!*/ expression, BindingRestrictions/*!*/ restrictions, PythonFunction/*!*/ value)
  38. : base(expression, BindingRestrictions.Empty, value) {
  39. Assert.NotNull(value);
  40. }
  41. #region IPythonInvokable Members
  42. public DynamicMetaObject/*!*/ Invoke(PythonInvokeBinder/*!*/ pythonInvoke, Expression/*!*/ codeContext, DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args) {
  43. return new FunctionBinderHelper(pythonInvoke, this, codeContext, args).MakeMetaObject();
  44. }
  45. #endregion
  46. #region IPythonGetable Members
  47. public DynamicMetaObject GetMember(PythonGetMemberBinder member, DynamicMetaObject codeContext) {
  48. return BindGetMemberWorker(member, member.Name, codeContext);
  49. }
  50. #endregion
  51. #region MetaObject Overrides
  52. public override DynamicMetaObject/*!*/ BindInvokeMember(InvokeMemberBinder/*!*/ action, DynamicMetaObject/*!*/[]/*!*/ args) {
  53. ParameterExpression tmp = Expression.Parameter(typeof(object));
  54. // first get the default binder value
  55. DynamicMetaObject fallback = action.FallbackInvokeMember(this, args);
  56. // then fallback w/ an error suggestion that does a late bound lookup.
  57. return action.FallbackInvokeMember(
  58. this,
  59. args,
  60. new DynamicMetaObject(
  61. Ast.Block(
  62. new[] { tmp },
  63. Ast.Condition(
  64. Ast.NotEqual(
  65. Ast.Assign(
  66. tmp,
  67. Ast.Call(
  68. typeof(PythonOps).GetMethod("PythonFunctionGetMember"),
  69. AstUtils.Convert(
  70. Expression,
  71. typeof(PythonFunction)
  72. ),
  73. Expression.Constant(action.Name)
  74. )
  75. ),
  76. Ast.Constant(OperationFailed.Value)
  77. ),
  78. action.FallbackInvoke(
  79. new DynamicMetaObject(tmp, BindingRestrictions.Empty),
  80. args,
  81. null
  82. ).Expression,
  83. AstUtils.Convert(fallback.Expression, typeof(object))
  84. )
  85. ),
  86. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions)
  87. )
  88. );
  89. }
  90. public override DynamicMetaObject/*!*/ BindInvoke(InvokeBinder/*!*/ call, params DynamicMetaObject/*!*/[]/*!*/ args) {
  91. return new FunctionBinderHelper(call, this, null, args).MakeMetaObject();
  92. }
  93. public override DynamicMetaObject BindConvert(ConvertBinder/*!*/ conversion) {
  94. return ConvertWorker(conversion, conversion.Type, conversion.Explicit ? ConversionResultKind.ExplicitCast : ConversionResultKind.ImplicitCast);
  95. }
  96. public DynamicMetaObject BindConvert(PythonConversionBinder binder) {
  97. return ConvertWorker(binder, binder.Type, binder.ResultKind);
  98. }
  99. public DynamicMetaObject ConvertWorker(DynamicMetaObjectBinder binder, Type type, ConversionResultKind kind) {
  100. if (type.IsSubclassOf(typeof(Delegate))) {
  101. return MakeDelegateTarget(binder, type, Restrict(typeof(PythonFunction)));
  102. }
  103. return FallbackConvert(binder);
  104. }
  105. public override System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames() {
  106. foreach (object o in Value.__dict__.Keys) {
  107. if (o is string) {
  108. yield return (string)o;
  109. }
  110. }
  111. }
  112. public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
  113. return BindGetMemberWorker(binder, binder.Name, PythonContext.GetCodeContextMO(binder));
  114. }
  115. private DynamicMetaObject BindGetMemberWorker(DynamicMetaObjectBinder binder, string name, DynamicMetaObject codeContext) {
  116. ParameterExpression tmp = Expression.Parameter(typeof(object));
  117. // first get the default binder value
  118. DynamicMetaObject fallback = FallbackGetMember(binder, this, codeContext);
  119. // then fallback w/ an error suggestion that does a late bound lookup.
  120. return FallbackGetMember(
  121. binder,
  122. this,
  123. codeContext,
  124. new DynamicMetaObject(
  125. Ast.Block(
  126. new[] { tmp },
  127. Ast.Condition(
  128. Ast.NotEqual(
  129. Ast.Assign(
  130. tmp,
  131. Ast.Call(
  132. typeof(PythonOps).GetMethod("PythonFunctionGetMember"),
  133. AstUtils.Convert(
  134. Expression,
  135. typeof(PythonFunction)
  136. ),
  137. Expression.Constant(name)
  138. )
  139. ),
  140. Ast.Constant(OperationFailed.Value)
  141. ),
  142. tmp,
  143. AstUtils.Convert(fallback.Expression, typeof(object))
  144. )
  145. ),
  146. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions)
  147. )
  148. );
  149. }
  150. private static DynamicMetaObject FallbackGetMember(DynamicMetaObjectBinder binder, DynamicMetaObject self, DynamicMetaObject codeContext) {
  151. return FallbackGetMember(binder, self, codeContext, null);
  152. }
  153. private static DynamicMetaObject FallbackGetMember(DynamicMetaObjectBinder binder, DynamicMetaObject self, DynamicMetaObject codeContext, DynamicMetaObject errorSuggestion) {
  154. PythonGetMemberBinder pyGetMem = binder as PythonGetMemberBinder;
  155. if (pyGetMem != null) {
  156. return pyGetMem.Fallback(self, codeContext, errorSuggestion);
  157. }
  158. return ((GetMemberBinder)binder).FallbackGetMember(self, errorSuggestion);
  159. }
  160. public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
  161. ParameterExpression tmp = Expression.Parameter(typeof(bool));
  162. // fallback w/ an error suggestion that does a late bound set
  163. return binder.FallbackSetMember(
  164. this,
  165. value,
  166. new DynamicMetaObject(
  167. Ast.Call(
  168. typeof(PythonOps).GetMethod("PythonFunctionSetMember"),
  169. AstUtils.Convert(
  170. Expression,
  171. typeof(PythonFunction)
  172. ),
  173. Expression.Constant(binder.Name),
  174. AstUtils.Convert(
  175. value.Expression,
  176. typeof(object)
  177. )
  178. ),
  179. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  180. )
  181. );
  182. }
  183. public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
  184. switch (binder.Name) {
  185. case "func_dict":
  186. case "__dict__":
  187. return new DynamicMetaObject(
  188. Expression.Call(
  189. typeof(PythonOps).GetMethod("PythonFunctionDeleteDict")
  190. ),
  191. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  192. );
  193. case "__doc__":
  194. case "func_doc":
  195. return new DynamicMetaObject(
  196. Expression.Call(
  197. typeof(PythonOps).GetMethod("PythonFunctionDeleteDoc"),
  198. Expression.Convert(Expression, typeof(PythonFunction))
  199. ),
  200. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  201. );
  202. case "func_defaults":
  203. return new DynamicMetaObject(
  204. Expression.Call(
  205. typeof(PythonOps).GetMethod("PythonFunctionDeleteDefaults"),
  206. Expression.Convert(Expression, typeof(PythonFunction))
  207. ),
  208. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  209. );
  210. }
  211. // first get the default binder value
  212. DynamicMetaObject fallback = binder.FallbackDeleteMember(this);
  213. // then fallback w/ an error suggestion that does a late bound delete
  214. return binder.FallbackDeleteMember(
  215. this,
  216. new DynamicMetaObject(
  217. Expression.Condition(
  218. Ast.Call(
  219. typeof(PythonOps).GetMethod("PythonFunctionDeleteMember"),
  220. AstUtils.Convert(
  221. Expression,
  222. typeof(PythonFunction)
  223. ),
  224. Expression.Constant(binder.Name)
  225. ),
  226. Expression.Default(typeof(void)), // we deleted the member
  227. AstUtils.Convert(
  228. fallback.Expression, // report language specific error,
  229. typeof(void)
  230. )
  231. ),
  232. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions)
  233. )
  234. );
  235. }
  236. #endregion
  237. #region Calls
  238. /// <summary>
  239. /// Performs the actual work of binding to the function.
  240. ///
  241. /// Overall this works by going through the arguments and attempting to bind all the outstanding known
  242. /// arguments - position arguments and named arguments which map to parameters are easy and handled
  243. /// in the 1st pass for GetArgumentsForRule. We also pick up any extra named or position arguments which
  244. /// will need to be passed off to a kw argument or a params array.
  245. ///
  246. /// After all the normal args have been assigned to do a 2nd pass in FinishArguments. Here we assign
  247. /// a value to either a value from the params list, kw-dict, or defaults. If there is ambiguity between
  248. /// this (e.g. we have a splatted params list, kw-dict, and defaults) we call a helper which extracts them
  249. /// in the proper order (first try the list, then the dict, then the defaults).
  250. /// </summary>
  251. class FunctionBinderHelper {
  252. private readonly MetaPythonFunction/*!*/ _func; // the meta object for the function we're calling
  253. private readonly DynamicMetaObject/*!*/[]/*!*/ _args; // the arguments for the function
  254. private readonly DynamicMetaObject/*!*/[]/*!*/ _originalArgs; // the original arguments for the function
  255. private readonly DynamicMetaObjectBinder/*!*/ _call; // the signature for the method call
  256. private readonly Expression _codeContext; // the code context expression if one is available.
  257. private List<ParameterExpression>/*!*/ _temps; // temporary variables allocated to create the rule
  258. private ParameterExpression _dict, _params, _paramsLen; // splatted dictionary & params + the initial length of the params array, null if not provided.
  259. private List<Expression> _init; // a set of initialization code (e.g. creating a list for the params array)
  260. private Expression _error; // a custom error expression if the default needs to be overridden.
  261. private bool _extractedParams; // true if we needed to extract a parameter from the parameter list.
  262. private bool _extractedKeyword; // true if we needed to extract a parameter from the kw list.
  263. private Expression _deferTest; // non-null if we have a test which could fail at runtime and we need to fallback to deferal
  264. private Expression _userProvidedParams; // expression the user provided that should be expanded for params.
  265. private Expression _paramlessCheck; // tests when we have no parameters
  266. public FunctionBinderHelper(DynamicMetaObjectBinder/*!*/ call, MetaPythonFunction/*!*/ function, Expression codeContext, DynamicMetaObject/*!*/[]/*!*/ args) {
  267. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "PythonFunction Invoke " + function.Value.FunctionCompatibility + " w/ " + args.Length + " args");
  268. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "PythonFunction");
  269. _call = call;
  270. _func = function;
  271. _args = args;
  272. _originalArgs = args;
  273. _temps = new List<ParameterExpression>();
  274. _codeContext = codeContext;
  275. // Remove the passed in instance argument if present
  276. int instanceIndex = Signature.IndexOf(ArgumentType.Instance);
  277. if (instanceIndex > -1) {
  278. _args = ArrayUtils.RemoveAt(_args, instanceIndex);
  279. }
  280. }
  281. public DynamicMetaObject/*!*/ MakeMetaObject() {
  282. Expression[] invokeArgs = GetArgumentsForRule();
  283. BindingRestrictions restrict = _func.Restrictions.Merge(GetRestrictions().Merge(BindingRestrictions.Combine(_args)));
  284. DynamicMetaObject res;
  285. if (invokeArgs != null) {
  286. // successful call
  287. Expression target = AddInitialization(MakeFunctionInvoke(invokeArgs));
  288. if (_temps.Count > 0) {
  289. target = Ast.Block(
  290. _temps,
  291. target
  292. );
  293. }
  294. res = new DynamicMetaObject(
  295. target,
  296. restrict
  297. );
  298. } else if (_error != null) {
  299. // custom error generated while figuring out the call
  300. res = new DynamicMetaObject(_error, restrict);
  301. } else {
  302. // generic error
  303. res = new DynamicMetaObject(
  304. Ast.Throw(
  305. Ast.Call(
  306. typeof(PythonOps).GetMethod(Signature.HasKeywordArgument() ? "BadKeywordArgumentError" : "FunctionBadArgumentError"),
  307. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  308. AstUtils.Constant(Signature.GetProvidedPositionalArgumentCount())
  309. ),
  310. typeof(object)
  311. ),
  312. restrict
  313. );
  314. }
  315. DynamicMetaObject[] deferArgs = ArrayUtils.Insert(_func, _originalArgs);
  316. if (_codeContext != null) {
  317. deferArgs = ArrayUtils.Insert(new DynamicMetaObject(_codeContext, BindingRestrictions.Empty), deferArgs);
  318. }
  319. return BindingHelpers.AddDynamicTestAndDefer(
  320. _call,
  321. res,
  322. deferArgs,
  323. new ValidationInfo(_deferTest),
  324. res.Expression.Type // force defer to our return type, our restrictions guarantee this to be true (only defaults can change, and we restrict to the delegate type)
  325. );
  326. }
  327. private CallSignature Signature {
  328. get {
  329. return BindingHelpers.GetCallSignature(_call);
  330. }
  331. }
  332. /// <summary>
  333. /// Makes the test for our rule.
  334. /// </summary>
  335. private BindingRestrictions/*!*/ GetRestrictions() {
  336. if (!Signature.HasKeywordArgument()) {
  337. return GetSimpleRestriction();
  338. }
  339. return GetComplexRestriction();
  340. }
  341. /// <summary>
  342. /// Makes the test when we just have simple positional arguments.
  343. /// </summary>
  344. private BindingRestrictions/*!*/ GetSimpleRestriction() {
  345. _deferTest = Ast.Equal(
  346. Ast.Call(
  347. typeof(PythonOps).GetMethod("FunctionGetCompatibility"),
  348. Ast.Convert(_func.Expression, typeof(PythonFunction))
  349. ),
  350. AstUtils.Constant(_func.Value.FunctionCompatibility)
  351. );
  352. return BindingRestrictionsHelpers.GetRuntimeTypeRestriction(
  353. _func.Expression, typeof(PythonFunction)
  354. );
  355. }
  356. /// <summary>
  357. /// Makes the test when we have a keyword argument call or splatting.
  358. /// </summary>
  359. /// <returns></returns>
  360. private BindingRestrictions/*!*/ GetComplexRestriction() {
  361. if (_extractedKeyword) {
  362. return BindingRestrictions.GetInstanceRestriction(_func.Expression, _func.Value);
  363. }
  364. return GetSimpleRestriction();
  365. }
  366. /// <summary>
  367. /// Gets the array of expressions which correspond to each argument for the function. These
  368. /// correspond with the function as it's defined in Python and must be transformed for our
  369. /// delegate type before being used.
  370. /// </summary>
  371. private Expression/*!*/[]/*!*/ GetArgumentsForRule() {
  372. Expression[] exprArgs = new Expression[_func.Value.NormalArgumentCount + _func.Value.ExtraArguments];
  373. List<Expression> extraArgs = null;
  374. Dictionary<string, Expression> namedArgs = null;
  375. int instanceIndex = Signature.IndexOf(ArgumentType.Instance);
  376. // walk all the provided args and find out where they go...
  377. for (int i = 0; i < _args.Length; i++) {
  378. int parameterIndex = (instanceIndex == -1 || i < instanceIndex) ? i : i + 1;
  379. switch (Signature.GetArgumentKind(i)) {
  380. case ArgumentType.Dictionary:
  381. _args[parameterIndex] = MakeDictionaryCopy(_args[parameterIndex]);
  382. continue;
  383. case ArgumentType.List:
  384. _userProvidedParams = _args[parameterIndex].Expression;
  385. continue;
  386. case ArgumentType.Named:
  387. _extractedKeyword = true;
  388. bool foundName = false;
  389. for (int j = 0; j < _func.Value.NormalArgumentCount; j++) {
  390. if (_func.Value.ArgNames[j] == Signature.GetArgumentName(i)) {
  391. if (exprArgs[j] != null) {
  392. // kw-argument provided for already provided normal argument.
  393. if (_error == null) {
  394. _error = Expression.Throw(
  395. Expression.Call(
  396. typeof(PythonOps).GetMethod("MultipleKeywordArgumentError"),
  397. GetFunctionParam(),
  398. Expression.Constant(_func.Value.ArgNames[j])
  399. ),
  400. typeof(object)
  401. );
  402. }
  403. return null;
  404. }
  405. exprArgs[j] = _args[parameterIndex].Expression;
  406. foundName = true;
  407. break;
  408. }
  409. }
  410. if (!foundName) {
  411. if (namedArgs == null) {
  412. namedArgs = new Dictionary<string, Expression>();
  413. }
  414. namedArgs[Signature.GetArgumentName(i)] = _args[parameterIndex].Expression;
  415. }
  416. continue;
  417. }
  418. if (i < _func.Value.NormalArgumentCount) {
  419. exprArgs[i] = _args[parameterIndex].Expression;
  420. } else {
  421. if (extraArgs == null) {
  422. extraArgs = new List<Expression>();
  423. }
  424. extraArgs.Add(_args[parameterIndex].Expression);
  425. }
  426. }
  427. if (!FinishArguments(exprArgs, extraArgs, namedArgs)) {
  428. if (namedArgs != null && _func.Value.ExpandDictPosition == -1) {
  429. MakeUnexpectedKeywordError(namedArgs);
  430. }
  431. return null;
  432. }
  433. return GetArgumentsForTargetType(exprArgs);
  434. }
  435. /// <summary>
  436. /// Binds any missing arguments to values from params array, kw dictionary, or default values.
  437. /// </summary>
  438. private bool FinishArguments(Expression[] exprArgs, List<Expression> paramsArgs, Dictionary<string, Expression> namedArgs) {
  439. int noDefaults = _func.Value.NormalArgumentCount - _func.Value.Defaults.Length; // number of args w/o defaults
  440. for (int i = 0; i < _func.Value.NormalArgumentCount; i++) {
  441. if (exprArgs[i] != null) {
  442. if (_userProvidedParams != null && i >= Signature.GetProvidedPositionalArgumentCount()) {
  443. exprArgs[i] = ValidateNotDuplicate(exprArgs[i], _func.Value.ArgNames[i], i);
  444. }
  445. continue;
  446. }
  447. if (i < noDefaults) {
  448. exprArgs[i] = ExtractNonDefaultValue(_func.Value.ArgNames[i]);
  449. if (exprArgs[i] == null) {
  450. // can't get a value, this is an invalid call.
  451. return false;
  452. }
  453. } else {
  454. exprArgs[i] = ExtractDefaultValue(i, i - noDefaults);
  455. }
  456. }
  457. if (!TryFinishList(exprArgs, paramsArgs) ||
  458. !TryFinishDictionary(exprArgs, namedArgs))
  459. return false;
  460. // add check for extra parameters.
  461. AddCheckForNoExtraParameters(exprArgs);
  462. return true;
  463. }
  464. /// <summary>
  465. /// Creates the argument for the list expansion parameter.
  466. /// </summary>
  467. private bool TryFinishList(Expression[] exprArgs, List<Expression> paramsArgs) {
  468. if (_func.Value.ExpandListPosition != -1) {
  469. if (_userProvidedParams != null) {
  470. if (_params == null && paramsArgs == null) {
  471. // we didn't extract any params, we can re-use a Tuple or
  472. // make a single copy.
  473. exprArgs[_func.Value.ExpandListPosition] = Ast.Call(
  474. typeof(PythonOps).GetMethod("GetOrCopyParamsTuple"),
  475. GetFunctionParam(),
  476. AstUtils.Convert(_userProvidedParams, typeof(object))
  477. );
  478. } else {
  479. // user provided a sequence to be expanded, and we may have used it,
  480. // or we have extra args.
  481. EnsureParams();
  482. exprArgs[_func.Value.ExpandListPosition] = Ast.Call(
  483. typeof(PythonOps).GetMethod("MakeTupleFromSequence"),
  484. AstUtils.Convert(_params, typeof(object))
  485. );
  486. if (paramsArgs != null) {
  487. MakeParamsAddition(paramsArgs);
  488. }
  489. }
  490. } else {
  491. exprArgs[_func.Value.ExpandListPosition] = MakeParamsTuple(paramsArgs);
  492. }
  493. } else if (paramsArgs != null) {
  494. // extra position args which are unused and no where to put them.
  495. return false;
  496. }
  497. return true;
  498. }
  499. /// <summary>
  500. /// Adds extra positional arguments to the start of the expanded list.
  501. /// </summary>
  502. private void MakeParamsAddition(List<Expression> paramsArgs) {
  503. _extractedParams = true;
  504. List<Expression> args = new List<Expression>(paramsArgs.Count + 1);
  505. args.Add(_params);
  506. args.AddRange(paramsArgs);
  507. EnsureInit();
  508. _init.Add(
  509. AstUtils.ComplexCallHelper(
  510. typeof(PythonOps).GetMethod("AddParamsArguments"),
  511. args.ToArray()
  512. )
  513. );
  514. }
  515. /// <summary>
  516. /// Creates the argument for the dictionary expansion parameter.
  517. /// </summary>
  518. private bool TryFinishDictionary(Expression[] exprArgs, Dictionary<string, Expression> namedArgs) {
  519. if (_func.Value.ExpandDictPosition != -1) {
  520. if (_dict != null) {
  521. // used provided a dictionary to be expanded
  522. exprArgs[_func.Value.ExpandDictPosition] = _dict;
  523. if (namedArgs != null) {
  524. foreach (KeyValuePair<string, Expression> kvp in namedArgs) {
  525. MakeDictionaryAddition(kvp);
  526. }
  527. }
  528. } else {
  529. exprArgs[_func.Value.ExpandDictPosition] = MakeDictionary(namedArgs);
  530. }
  531. } else if (namedArgs != null) {
  532. // extra named args which are unused and no where to put them.
  533. return false;
  534. }
  535. return true;
  536. }
  537. /// <summary>
  538. /// Adds an unbound keyword argument into the dictionary.
  539. /// </summary>
  540. /// <param name="kvp"></param>
  541. private void MakeDictionaryAddition(KeyValuePair<string, Expression> kvp) {
  542. _init.Add(
  543. Ast.Call(
  544. typeof(PythonOps).GetMethod("AddDictionaryArgument"),
  545. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  546. AstUtils.Constant(kvp.Key),
  547. AstUtils.Convert(kvp.Value, typeof(object)),
  548. AstUtils.Convert(_dict, typeof(PythonDictionary))
  549. )
  550. );
  551. }
  552. /// <summary>
  553. /// Adds a check to the last parameter (so it's evaluated after we've extracted
  554. /// all the parameters) to ensure that we don't have any extra params or kw-params
  555. /// when we don't have a params array or params dict to expand them into.
  556. /// </summary>
  557. private void AddCheckForNoExtraParameters(Expression[] exprArgs) {
  558. List<Expression> tests = new List<Expression>(3);
  559. // test we've used all of the extra parameters
  560. if (_func.Value.ExpandListPosition == -1) {
  561. if (_params != null) {
  562. // we used some params, they should have gone down to zero...
  563. tests.Add(
  564. Ast.Call(
  565. typeof(PythonOps).GetMethod("CheckParamsZero"),
  566. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  567. _params
  568. )
  569. );
  570. } else if (_userProvidedParams != null) {
  571. // the user provided params, we didn't need any, and they should be zero
  572. tests.Add(
  573. Ast.Call(
  574. typeof(PythonOps).GetMethod("CheckUserParamsZero"),
  575. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  576. AstUtils.Convert(_userProvidedParams, typeof(object))
  577. )
  578. );
  579. }
  580. }
  581. // test that we've used all the extra named arguments
  582. if (_func.Value.ExpandDictPosition == -1 && _dict != null) {
  583. tests.Add(
  584. Ast.Call(
  585. typeof(PythonOps).GetMethod("CheckDictionaryZero"),
  586. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  587. AstUtils.Convert(_dict, typeof(IDictionary))
  588. )
  589. );
  590. }
  591. if (tests.Count != 0) {
  592. if (exprArgs.Length != 0) {
  593. // if we have arguments run the tests after the last arg is evaluated.
  594. Expression last = exprArgs[exprArgs.Length - 1];
  595. ParameterExpression temp;
  596. _temps.Add(temp = Ast.Variable(last.Type, "$temp"));
  597. tests.Insert(0, Ast.Assign(temp, last));
  598. tests.Add(temp);
  599. exprArgs[exprArgs.Length - 1] = Ast.Block(tests.ToArray());
  600. } else {
  601. // otherwise run them right before the method call
  602. _paramlessCheck = Ast.Block(tests.ToArray());
  603. }
  604. }
  605. }
  606. /// <summary>
  607. /// Helper function to validate that a named arg isn't duplicated with by
  608. /// a params list or the dictionary (or both).
  609. /// </summary>
  610. private Expression ValidateNotDuplicate(Expression value, string name, int position) {
  611. EnsureParams();
  612. return Ast.Block(
  613. Ast.Call(
  614. typeof(PythonOps).GetMethod("VerifyUnduplicatedByPosition"),
  615. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  616. AstUtils.Constant(name, typeof(string)), // name
  617. AstUtils.Constant(position), // position
  618. _paramsLen // params list length
  619. ),
  620. value
  621. );
  622. }
  623. /// <summary>
  624. /// Helper function to get a value (which has no default) from either the
  625. /// params list or the dictionary (or both).
  626. /// </summary>
  627. private Expression ExtractNonDefaultValue(string name) {
  628. if (_userProvidedParams != null) {
  629. // expanded params
  630. if (_dict != null) {
  631. // expanded params & dict
  632. return ExtractFromListOrDictionary(name);
  633. } else {
  634. return ExtractNextParamsArg();
  635. }
  636. } else if (_dict != null) {
  637. // expanded dict
  638. return ExtractDictionaryArgument(name);
  639. }
  640. // missing argument, no default, no expanded params or dict.
  641. return null;
  642. }
  643. /// <summary>
  644. /// Helper function to get the specified variable from the dictionary.
  645. /// </summary>
  646. private Expression ExtractDictionaryArgument(string name) {
  647. _extractedKeyword = true;
  648. return Ast.Call(
  649. typeof(PythonOps).GetMethod("ExtractDictionaryArgument"),
  650. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  651. AstUtils.Constant(name, typeof(string)), // name
  652. AstUtils.Constant(Signature.ArgumentCount), // arg count
  653. AstUtils.Convert(_dict, typeof(PythonDictionary)) // dictionary
  654. );
  655. }
  656. /// <summary>
  657. /// Helper function to extract the variable from defaults, or to call a helper
  658. /// to check params / kw-dict / defaults to see which one contains the actual value.
  659. /// </summary>
  660. private Expression ExtractDefaultValue(int index, int dfltIndex) {
  661. if (_dict == null && _userProvidedParams == null) {
  662. // we can pull the default directly
  663. return Ast.Call(
  664. typeof(PythonOps).GetMethod("FunctionGetDefaultValue"),
  665. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  666. AstUtils.Constant(dfltIndex)
  667. );
  668. } else {
  669. // we might have a conflict, check the default last.
  670. if (_userProvidedParams != null) {
  671. EnsureParams();
  672. }
  673. _extractedKeyword = true;
  674. return Ast.Call(
  675. typeof(PythonOps).GetMethod("GetFunctionParameterValue"),
  676. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  677. AstUtils.Constant(dfltIndex),
  678. AstUtils.Constant(_func.Value.ArgNames[index], typeof(string)),
  679. VariableOrNull(_params, typeof(List)),
  680. VariableOrNull(_dict, typeof(PythonDictionary))
  681. );
  682. }
  683. }
  684. /// <summary>
  685. /// Helper function to extract from the params list or dictionary depending upon
  686. /// which one has an available value.
  687. /// </summary>
  688. private Expression ExtractFromListOrDictionary(string name) {
  689. EnsureParams();
  690. _extractedKeyword = true;
  691. return Ast.Call(
  692. typeof(PythonOps).GetMethod("ExtractAnyArgument"),
  693. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  694. AstUtils.Constant(name, typeof(string)), // name
  695. _paramsLen, // arg count
  696. _params, // params list
  697. AstUtils.Convert(_dict, typeof(IDictionary)) // dictionary
  698. );
  699. }
  700. private void EnsureParams() {
  701. if (!_extractedParams) {
  702. Debug.Assert(_userProvidedParams != null);
  703. MakeParamsCopy(_userProvidedParams);
  704. _extractedParams = true;
  705. }
  706. }
  707. /// <summary>
  708. /// Helper function to extract the next argument from the params list.
  709. /// </summary>
  710. private Expression ExtractNextParamsArg() {
  711. if (!_extractedParams) {
  712. MakeParamsCopy(_userProvidedParams);
  713. _extractedParams = true;
  714. }
  715. return Ast.Call(
  716. typeof(PythonOps).GetMethod("ExtractParamsArgument"),
  717. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  718. AstUtils.Constant(Signature.ArgumentCount), // arg count
  719. _params // list
  720. );
  721. }
  722. private static Expression VariableOrNull(ParameterExpression var, Type type) {
  723. if (var != null) {
  724. return AstUtils.Convert(
  725. var,
  726. type
  727. );
  728. }
  729. return AstUtils.Constant(null, type);
  730. }
  731. /// <summary>
  732. /// Fixes up the argument list for the appropriate target delegate type.
  733. /// </summary>
  734. private Expression/*!*/[]/*!*/ GetArgumentsForTargetType(Expression[] exprArgs) {
  735. Type target = _func.Value.func_code.Target.GetType();
  736. if (target == typeof(Func<PythonFunction, object[], object>)) {
  737. exprArgs = new Expression[] {
  738. AstUtils.NewArrayHelper(typeof(object), exprArgs)
  739. };
  740. }
  741. return exprArgs;
  742. }
  743. /// <summary>
  744. /// Helper function to get the function argument strongly typed.
  745. /// </summary>
  746. private UnaryExpression/*!*/ GetFunctionParam() {
  747. return Ast.Convert(_func.Expression, typeof(PythonFunction));
  748. }
  749. /// <summary>
  750. /// Called when the user is expanding a dictionary - we copy the user
  751. /// dictionary and verify that it contains only valid string names.
  752. /// </summary>
  753. private DynamicMetaObject/*!*/ MakeDictionaryCopy(DynamicMetaObject/*!*/ userDict) {
  754. Debug.Assert(_dict == null);
  755. userDict = userDict.Restrict(userDict.GetLimitType());
  756. _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict"));
  757. EnsureInit();
  758. string methodName;
  759. if (typeof(PythonDictionary).IsAssignableFrom(userDict.GetLimitType())) {
  760. methodName = "CopyAndVerifyPythonDictionary";
  761. } else if (typeof(IDictionary).IsAssignableFrom(userDict.GetLimitType())) {
  762. methodName = "CopyAndVerifyDictionary";
  763. } else {
  764. methodName = "CopyAndVerifyUserMapping";
  765. }
  766. _init.Add(
  767. Ast.Assign(
  768. _dict,
  769. Ast.Call(
  770. typeof(PythonOps).GetMethod(methodName),
  771. GetFunctionParam(),
  772. AstUtils.Convert(userDict.Expression, userDict.GetLimitType())
  773. )
  774. )
  775. );
  776. return userDict;
  777. }
  778. /// <summary>
  779. /// Called when the user is expanding a params argument
  780. /// </summary>
  781. private void MakeParamsCopy(Expression/*!*/ userList) {
  782. Debug.Assert(_params == null);
  783. _temps.Add(_params = Ast.Variable(typeof(List), "$list"));
  784. _temps.Add(_paramsLen = Ast.Variable(typeof(int), "$paramsLen"));
  785. EnsureInit();
  786. _init.Add(
  787. Ast.Assign(
  788. _params,
  789. Ast.Call(
  790. typeof(PythonOps).GetMethod("CopyAndVerifyParamsList"),
  791. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  792. AstUtils.Convert(userList, typeof(object))
  793. )
  794. )
  795. );
  796. _init.Add(
  797. Ast.Assign(_paramsLen,
  798. Ast.Add(
  799. Ast.Call(_params, typeof(List).GetMethod("__len__")),
  800. AstUtils.Constant(Signature.GetProvidedPositionalArgumentCount())
  801. )
  802. )
  803. );
  804. }
  805. /// <summary>
  806. /// Called when the user hasn't supplied a dictionary to be expanded but the
  807. /// function takes a dictionary to be expanded.
  808. /// </summary>
  809. private Expression MakeDictionary(Dictionary<string, Expression/*!*/> namedArgs) {
  810. Debug.Assert(_dict == null);
  811. _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict"));
  812. Expression dictCreator;
  813. ParameterExpression dictRef = _dict;
  814. if (namedArgs != null) {
  815. Debug.Assert(namedArgs.Count > 0);
  816. Expression[] items = new Expression[namedArgs.Count * 2];
  817. int itemIndex = 0;
  818. foreach (KeyValuePair<string, Expression> kvp in namedArgs) {
  819. items[itemIndex++] = AstUtils.Convert(kvp.Value, typeof(object));
  820. items[itemIndex++] = AstUtils.Constant(kvp.Key, typeof(object));
  821. }
  822. dictCreator = Ast.Assign(
  823. _dict,
  824. Ast.Call(
  825. typeof(PythonOps).GetMethod("MakeHomogeneousDictFromItems"),
  826. Ast.NewArrayInit(typeof(object), items)
  827. )
  828. );
  829. } else {
  830. dictCreator = Ast.Assign(
  831. _dict,
  832. Ast.Call(
  833. typeof(PythonOps).GetMethod("MakeDict"),
  834. AstUtils.Constant(0)
  835. )
  836. );
  837. }
  838. return dictCreator;
  839. }
  840. /// <summary>
  841. /// Helper function to create the expression for creating the actual tuple passed through.
  842. /// </summary>
  843. private static Expression/*!*/ MakeParamsTuple(List<Expression> extraArgs) {
  844. if (extraArgs != null) {
  845. return AstUtils.ComplexCallHelper(
  846. typeof(PythonOps).GetMethod("MakeTuple"),
  847. extraArgs.ToArray()
  848. );
  849. }
  850. return Ast.Call(
  851. typeof(PythonOps).GetMethod("MakeTuple"),
  852. Ast.NewArrayInit(typeof(object[]))
  853. );
  854. }
  855. /// <summary>
  856. /// Creates the code to invoke the target delegate function w/ the specified arguments.
  857. /// </summary>
  858. private Expression/*!*/ MakeFunctionInvoke(Expression[] invokeArgs) {
  859. Type targetType = _func.Value.func_code.Target.GetType();
  860. MethodInfo method = targetType.GetMethod("Invoke");
  861. // If calling generator, create the instance of PythonGenerator first
  862. // and add it into the list of arguments
  863. invokeArgs = ArrayUtils.Insert(GetFunctionParam(), invokeArgs);
  864. Expression invoke = AstUtils.SimpleCallHelper(
  865. Ast.Convert(
  866. Ast.Call(
  867. typeof(PythonOps).GetMethod("FunctionGetTarget"),
  868. GetFunctionParam()
  869. ),
  870. targetType
  871. ),
  872. method,
  873. invokeArgs
  874. );
  875. if (_paramlessCheck != null) {
  876. invoke = Expression.Block(_paramlessCheck, invoke);
  877. }
  878. return invoke;
  879. }
  880. /// <summary>
  881. /// Appends the initialization code for the call to the function if any exists.
  882. /// </summary>
  883. private Expression/*!*/ AddInitialization(Expression body) {
  884. if (_init == null) return body;
  885. List<Expression> res = new List<Expression>(_init);
  886. res.Add(body);
  887. return Ast.Block(res);
  888. }
  889. private void MakeUnexpectedKeywordError(Dictionary<string, Expression> namedArgs) {
  890. string name = null;
  891. foreach (string id in namedArgs.Keys) {
  892. name = id;
  893. break;
  894. }
  895. _error = Ast.Throw(
  896. Ast.Call(
  897. typeof(PythonOps).GetMethod("UnexpectedKeywordArgumentError"),
  898. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  899. AstUtils.Constant(name, typeof(string))
  900. ),
  901. typeof(PythonOps)
  902. );
  903. }
  904. private void EnsureInit() {
  905. if (_init == null) _init = new List<Expression>();
  906. }
  907. }
  908. #endregion
  909. #region Operations
  910. private static DynamicMetaObject/*!*/ MakeCallSignatureRule(DynamicMetaObject self) {
  911. return new DynamicMetaObject(
  912. Ast.Call(
  913. typeof(PythonOps).GetMethod("GetFunctionSignature"),
  914. AstUtils.Convert(
  915. self.Expression,
  916. typeof(PythonFunction)
  917. )
  918. ),
  919. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction))
  920. );
  921. }
  922. private static DynamicMetaObject MakeIsCallableRule(DynamicMetaObject/*!*/ self) {
  923. return new DynamicMetaObject(
  924. AstUtils.Constant(true),
  925. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction))
  926. );
  927. }
  928. #endregion
  929. #region Helpers
  930. public new PythonFunction/*!*/ Value

Large files files are truncated, but you can click here to view the full file