PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonFunction.cs

http://github.com/IronLanguages/main
C# | 1150 lines | 848 code | 154 blank | 148 comment | 138 complexity | abd55e35c81ff556a98767bf30602f16 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception

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 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. #if FEATURE_CORE_DLR
  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. // fallback w/ an error suggestion that does a late bound set
  162. return binder.FallbackSetMember(
  163. this,
  164. value,
  165. new DynamicMetaObject(
  166. Ast.Call(
  167. typeof(PythonOps).GetMethod("PythonFunctionSetMember"),
  168. AstUtils.Convert(
  169. Expression,
  170. typeof(PythonFunction)
  171. ),
  172. Expression.Constant(binder.Name),
  173. AstUtils.Convert(
  174. value.Expression,
  175. typeof(object)
  176. )
  177. ),
  178. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  179. )
  180. );
  181. }
  182. public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
  183. switch (binder.Name) {
  184. case "func_dict":
  185. case "__dict__":
  186. return new DynamicMetaObject(
  187. Expression.Call(
  188. typeof(PythonOps).GetMethod("PythonFunctionDeleteDict")
  189. ),
  190. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  191. );
  192. case "__doc__":
  193. case "func_doc":
  194. return new DynamicMetaObject(
  195. Expression.Call(
  196. typeof(PythonOps).GetMethod("PythonFunctionDeleteDoc"),
  197. Expression.Convert(Expression, typeof(PythonFunction))
  198. ),
  199. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  200. );
  201. case "func_defaults":
  202. return new DynamicMetaObject(
  203. Expression.Call(
  204. typeof(PythonOps).GetMethod("PythonFunctionDeleteDefaults"),
  205. Expression.Convert(Expression, typeof(PythonFunction))
  206. ),
  207. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction))
  208. );
  209. }
  210. // first get the default binder value
  211. DynamicMetaObject fallback = binder.FallbackDeleteMember(this);
  212. // then fallback w/ an error suggestion that does a late bound delete
  213. return binder.FallbackDeleteMember(
  214. this,
  215. new DynamicMetaObject(
  216. Expression.Condition(
  217. Ast.Call(
  218. typeof(PythonOps).GetMethod("PythonFunctionDeleteMember"),
  219. AstUtils.Convert(
  220. Expression,
  221. typeof(PythonFunction)
  222. ),
  223. Expression.Constant(binder.Name)
  224. ),
  225. Expression.Default(typeof(void)), // we deleted the member
  226. AstUtils.Convert(
  227. fallback.Expression, // report language specific error,
  228. typeof(void)
  229. )
  230. ),
  231. BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions)
  232. )
  233. );
  234. }
  235. #endregion
  236. #region Calls
  237. /// <summary>
  238. /// Performs the actual work of binding to the function.
  239. ///
  240. /// Overall this works by going through the arguments and attempting to bind all the outstanding known
  241. /// arguments - position arguments and named arguments which map to parameters are easy and handled
  242. /// in the 1st pass for GetArgumentsForRule. We also pick up any extra named or position arguments which
  243. /// will need to be passed off to a kw argument or a params array.
  244. ///
  245. /// After all the normal args have been assigned to do a 2nd pass in FinishArguments. Here we assign
  246. /// a value to either a value from the params list, kw-dict, or defaults. If there is ambiguity between
  247. /// this (e.g. we have a splatted params list, kw-dict, and defaults) we call a helper which extracts them
  248. /// in the proper order (first try the list, then the dict, then the defaults).
  249. /// </summary>
  250. class FunctionBinderHelper {
  251. private readonly MetaPythonFunction/*!*/ _func; // the meta object for the function we're calling
  252. private readonly DynamicMetaObject/*!*/[]/*!*/ _args; // the arguments for the function
  253. private readonly DynamicMetaObject/*!*/[]/*!*/ _originalArgs; // the original arguments for the function
  254. private readonly DynamicMetaObjectBinder/*!*/ _call; // the signature for the method call
  255. private readonly Expression _codeContext; // the code context expression if one is available.
  256. private List<ParameterExpression>/*!*/ _temps; // temporary variables allocated to create the rule
  257. private ParameterExpression _dict, _params, _paramsLen; // splatted dictionary & params + the initial length of the params array, null if not provided.
  258. private List<Expression> _init; // a set of initialization code (e.g. creating a list for the params array)
  259. private Expression _error; // a custom error expression if the default needs to be overridden.
  260. private bool _extractedParams; // true if we needed to extract a parameter from the parameter list.
  261. private bool _extractedDefault; // true if we needed to extract a parameter from the kw list.
  262. private bool _needCodeTest; // true if we need to test the code object
  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. _call.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 (_extractedDefault) {
  362. return BindingRestrictions.GetInstanceRestriction(_func.Expression, _func.Value);
  363. } else if (_needCodeTest) {
  364. return GetSimpleRestriction().Merge(
  365. BindingRestrictions.GetInstanceRestriction(
  366. Expression.Property(
  367. GetFunctionParam(),
  368. "__code__"
  369. ),
  370. _func.Value.__code__
  371. )
  372. );
  373. }
  374. return GetSimpleRestriction();
  375. }
  376. /// <summary>
  377. /// Gets the array of expressions which correspond to each argument for the function. These
  378. /// correspond with the function as it's defined in Python and must be transformed for our
  379. /// delegate type before being used.
  380. /// </summary>
  381. private Expression/*!*/[]/*!*/ GetArgumentsForRule() {
  382. Expression[] exprArgs = new Expression[_func.Value.NormalArgumentCount + _func.Value.ExtraArguments];
  383. List<Expression> extraArgs = null;
  384. Dictionary<string, Expression> namedArgs = null;
  385. int instanceIndex = Signature.IndexOf(ArgumentType.Instance);
  386. // walk all the provided args and find out where they go...
  387. for (int i = 0; i < _args.Length; i++) {
  388. int parameterIndex = (instanceIndex == -1 || i < instanceIndex) ? i : i + 1;
  389. switch (Signature.GetArgumentKind(i)) {
  390. case ArgumentType.Dictionary:
  391. _args[parameterIndex] = MakeDictionaryCopy(_args[parameterIndex]);
  392. continue;
  393. case ArgumentType.List:
  394. _userProvidedParams = _args[parameterIndex].Expression;
  395. continue;
  396. case ArgumentType.Named:
  397. _needCodeTest = true;
  398. bool foundName = false;
  399. for (int j = 0; j < _func.Value.NormalArgumentCount; j++) {
  400. if (_func.Value.ArgNames[j] == Signature.GetArgumentName(i)) {
  401. if (exprArgs[j] != null) {
  402. // kw-argument provided for already provided normal argument.
  403. if (_error == null) {
  404. _error = _call.Throw(
  405. Expression.Call(
  406. typeof(PythonOps).GetMethod("MultipleKeywordArgumentError"),
  407. GetFunctionParam(),
  408. Expression.Constant(_func.Value.ArgNames[j])
  409. ),
  410. typeof(object)
  411. );
  412. }
  413. return null;
  414. }
  415. exprArgs[j] = _args[parameterIndex].Expression;
  416. foundName = true;
  417. break;
  418. }
  419. }
  420. if (!foundName) {
  421. if (namedArgs == null) {
  422. namedArgs = new Dictionary<string, Expression>();
  423. }
  424. namedArgs[Signature.GetArgumentName(i)] = _args[parameterIndex].Expression;
  425. }
  426. continue;
  427. }
  428. if (i < _func.Value.NormalArgumentCount) {
  429. exprArgs[i] = _args[parameterIndex].Expression;
  430. } else {
  431. if (extraArgs == null) {
  432. extraArgs = new List<Expression>();
  433. }
  434. extraArgs.Add(_args[parameterIndex].Expression);
  435. }
  436. }
  437. if (!FinishArguments(exprArgs, extraArgs, namedArgs)) {
  438. if (namedArgs != null && _func.Value.ExpandDictPosition == -1) {
  439. MakeUnexpectedKeywordError(namedArgs);
  440. }
  441. return null;
  442. }
  443. return GetArgumentsForTargetType(exprArgs);
  444. }
  445. /// <summary>
  446. /// Binds any missing arguments to values from params array, kw dictionary, or default values.
  447. /// </summary>
  448. private bool FinishArguments(Expression[] exprArgs, List<Expression> paramsArgs, Dictionary<string, Expression> namedArgs) {
  449. int noDefaults = _func.Value.NormalArgumentCount - _func.Value.Defaults.Length; // number of args w/o defaults
  450. for (int i = 0; i < _func.Value.NormalArgumentCount; i++) {
  451. if (exprArgs[i] != null) {
  452. if (_userProvidedParams != null && i >= Signature.GetProvidedPositionalArgumentCount()) {
  453. exprArgs[i] = ValidateNotDuplicate(exprArgs[i], _func.Value.ArgNames[i], i);
  454. }
  455. continue;
  456. }
  457. if (i < noDefaults) {
  458. exprArgs[i] = ExtractNonDefaultValue(_func.Value.ArgNames[i]);
  459. if (exprArgs[i] == null) {
  460. // can't get a value, this is an invalid call.
  461. return false;
  462. }
  463. } else {
  464. exprArgs[i] = ExtractDefaultValue(i, i - noDefaults);
  465. }
  466. }
  467. if (!TryFinishList(exprArgs, paramsArgs) ||
  468. !TryFinishDictionary(exprArgs, namedArgs))
  469. return false;
  470. // add check for extra parameters.
  471. AddCheckForNoExtraParameters(exprArgs);
  472. return true;
  473. }
  474. /// <summary>
  475. /// Creates the argument for the list expansion parameter.
  476. /// </summary>
  477. private bool TryFinishList(Expression[] exprArgs, List<Expression> paramsArgs) {
  478. if (_func.Value.ExpandListPosition != -1) {
  479. if (_userProvidedParams != null) {
  480. if (_params == null && paramsArgs == null) {
  481. // we didn't extract any params, we can re-use a Tuple or
  482. // make a single copy.
  483. exprArgs[_func.Value.ExpandListPosition] = Ast.Call(
  484. typeof(PythonOps).GetMethod("GetOrCopyParamsTuple"),
  485. GetFunctionParam(),
  486. AstUtils.Convert(_userProvidedParams, typeof(object))
  487. );
  488. } else {
  489. // user provided a sequence to be expanded, and we may have used it,
  490. // or we have extra args.
  491. EnsureParams();
  492. exprArgs[_func.Value.ExpandListPosition] = Ast.Call(
  493. typeof(PythonOps).GetMethod("MakeTupleFromSequence"),
  494. AstUtils.Convert(_params, typeof(object))
  495. );
  496. if (paramsArgs != null) {
  497. MakeParamsAddition(paramsArgs);
  498. }
  499. }
  500. } else {
  501. exprArgs[_func.Value.ExpandListPosition] = MakeParamsTuple(paramsArgs);
  502. }
  503. } else if (paramsArgs != null) {
  504. // extra position args which are unused and no where to put them.
  505. return false;
  506. }
  507. return true;
  508. }
  509. /// <summary>
  510. /// Adds extra positional arguments to the start of the expanded list.
  511. /// </summary>
  512. private void MakeParamsAddition(List<Expression> paramsArgs) {
  513. _extractedParams = true;
  514. List<Expression> args = new List<Expression>(paramsArgs.Count + 1);
  515. args.Add(_params);
  516. args.AddRange(paramsArgs);
  517. EnsureInit();
  518. _init.Add(
  519. AstUtils.ComplexCallHelper(
  520. typeof(PythonOps).GetMethod("AddParamsArguments"),
  521. args.ToArray()
  522. )
  523. );
  524. }
  525. /// <summary>
  526. /// Creates the argument for the dictionary expansion parameter.
  527. /// </summary>
  528. private bool TryFinishDictionary(Expression[] exprArgs, Dictionary<string, Expression> namedArgs) {
  529. if (_func.Value.ExpandDictPosition != -1) {
  530. if (_dict != null) {
  531. // used provided a dictionary to be expanded
  532. exprArgs[_func.Value.ExpandDictPosition] = _dict;
  533. if (namedArgs != null) {
  534. foreach (KeyValuePair<string, Expression> kvp in namedArgs) {
  535. MakeDictionaryAddition(kvp);
  536. }
  537. }
  538. } else {
  539. exprArgs[_func.Value.ExpandDictPosition] = MakeDictionary(namedArgs);
  540. }
  541. } else if (namedArgs != null) {
  542. // extra named args which are unused and no where to put them.
  543. return false;
  544. }
  545. return true;
  546. }
  547. /// <summary>
  548. /// Adds an unbound keyword argument into the dictionary.
  549. /// </summary>
  550. /// <param name="kvp"></param>
  551. private void MakeDictionaryAddition(KeyValuePair<string, Expression> kvp) {
  552. _init.Add(
  553. Ast.Call(
  554. typeof(PythonOps).GetMethod("AddDictionaryArgument"),
  555. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  556. AstUtils.Constant(kvp.Key),
  557. AstUtils.Convert(kvp.Value, typeof(object)),
  558. AstUtils.Convert(_dict, typeof(PythonDictionary))
  559. )
  560. );
  561. }
  562. /// <summary>
  563. /// Adds a check to the last parameter (so it's evaluated after we've extracted
  564. /// all the parameters) to ensure that we don't have any extra params or kw-params
  565. /// when we don't have a params array or params dict to expand them into.
  566. /// </summary>
  567. private void AddCheckForNoExtraParameters(Expression[] exprArgs) {
  568. List<Expression> tests = new List<Expression>(3);
  569. // test we've used all of the extra parameters
  570. if (_func.Value.ExpandListPosition == -1) {
  571. if (_params != null) {
  572. // we used some params, they should have gone down to zero...
  573. tests.Add(
  574. Ast.Call(
  575. typeof(PythonOps).GetMethod("CheckParamsZero"),
  576. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  577. _params
  578. )
  579. );
  580. } else if (_userProvidedParams != null) {
  581. // the user provided params, we didn't need any, and they should be zero
  582. tests.Add(
  583. Ast.Call(
  584. typeof(PythonOps).GetMethod("CheckUserParamsZero"),
  585. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  586. AstUtils.Convert(_userProvidedParams, typeof(object))
  587. )
  588. );
  589. }
  590. }
  591. // test that we've used all the extra named arguments
  592. if (_func.Value.ExpandDictPosition == -1 && _dict != null) {
  593. tests.Add(
  594. Ast.Call(
  595. typeof(PythonOps).GetMethod("CheckDictionaryZero"),
  596. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  597. AstUtils.Convert(_dict, typeof(IDictionary))
  598. )
  599. );
  600. }
  601. if (tests.Count != 0) {
  602. if (exprArgs.Length != 0) {
  603. // if we have arguments run the tests after the last arg is evaluated.
  604. Expression last = exprArgs[exprArgs.Length - 1];
  605. ParameterExpression temp;
  606. _temps.Add(temp = Ast.Variable(last.Type, "$temp"));
  607. tests.Insert(0, Ast.Assign(temp, last));
  608. tests.Add(temp);
  609. exprArgs[exprArgs.Length - 1] = Ast.Block(tests.ToArray());
  610. } else {
  611. // otherwise run them right before the method call
  612. _paramlessCheck = Ast.Block(tests.ToArray());
  613. }
  614. }
  615. }
  616. /// <summary>
  617. /// Helper function to validate that a named arg isn't duplicated with by
  618. /// a params list or the dictionary (or both).
  619. /// </summary>
  620. private Expression ValidateNotDuplicate(Expression value, string name, int position) {
  621. EnsureParams();
  622. return Ast.Block(
  623. Ast.Call(
  624. typeof(PythonOps).GetMethod("VerifyUnduplicatedByPosition"),
  625. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  626. AstUtils.Constant(name, typeof(string)), // name
  627. AstUtils.Constant(position), // position
  628. _paramsLen // params list length
  629. ),
  630. value
  631. );
  632. }
  633. /// <summary>
  634. /// Helper function to get a value (which has no default) from either the
  635. /// params list or the dictionary (or both).
  636. /// </summary>
  637. private Expression ExtractNonDefaultValue(string name) {
  638. if (_userProvidedParams != null) {
  639. // expanded params
  640. if (_dict != null) {
  641. // expanded params & dict
  642. return ExtractFromListOrDictionary(name);
  643. } else {
  644. return ExtractNextParamsArg();
  645. }
  646. } else if (_dict != null) {
  647. // expanded dict
  648. return ExtractDictionaryArgument(name);
  649. }
  650. // missing argument, no default, no expanded params or dict.
  651. return null;
  652. }
  653. /// <summary>
  654. /// Helper function to get the specified variable from the dictionary.
  655. /// </summary>
  656. private Expression ExtractDictionaryArgument(string name) {
  657. _needCodeTest = true;
  658. return Ast.Call(
  659. typeof(PythonOps).GetMethod("ExtractDictionaryArgument"),
  660. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  661. AstUtils.Constant(name, typeof(string)), // name
  662. AstUtils.Constant(Signature.ArgumentCount), // arg count
  663. AstUtils.Convert(_dict, typeof(PythonDictionary)) // dictionary
  664. );
  665. }
  666. /// <summary>
  667. /// Helper function to extract the variable from defaults, or to call a helper
  668. /// to check params / kw-dict / defaults to see which one contains the actual value.
  669. /// </summary>
  670. private Expression ExtractDefaultValue(int index, int dfltIndex) {
  671. if (_dict == null && _userProvidedParams == null) {
  672. // we can pull the default directly
  673. return Ast.Call(
  674. typeof(PythonOps).GetMethod("FunctionGetDefaultValue"),
  675. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  676. AstUtils.Constant(dfltIndex)
  677. );
  678. } else {
  679. // we might have a conflict, check the default last.
  680. if (_userProvidedParams != null) {
  681. EnsureParams();
  682. }
  683. _extractedDefault = true;
  684. return Ast.Call(
  685. typeof(PythonOps).GetMethod("GetFunctionParameterValue"),
  686. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  687. AstUtils.Constant(dfltIndex),
  688. AstUtils.Constant(_func.Value.ArgNames[index], typeof(string)),
  689. VariableOrNull(_params, typeof(List)),
  690. VariableOrNull(_dict, typeof(PythonDictionary))
  691. );
  692. }
  693. }
  694. /// <summary>
  695. /// Helper function to extract from the params list or dictionary depending upon
  696. /// which one has an available value.
  697. /// </summary>
  698. private Expression ExtractFromListOrDictionary(string name) {
  699. EnsureParams();
  700. _needCodeTest = true;
  701. return Ast.Call(
  702. typeof(PythonOps).GetMethod("ExtractAnyArgument"),
  703. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  704. AstUtils.Constant(name, typeof(string)), // name
  705. _paramsLen, // arg count
  706. _params, // params list
  707. AstUtils.Convert(_dict, typeof(IDictionary)) // dictionary
  708. );
  709. }
  710. private void EnsureParams() {
  711. if (!_extractedParams) {
  712. Debug.Assert(_userProvidedParams != null);
  713. MakeParamsCopy(_userProvidedParams);
  714. _extractedParams = true;
  715. }
  716. }
  717. /// <summary>
  718. /// Helper function to extract the next argument from the params list.
  719. /// </summary>
  720. private Expression ExtractNextParamsArg() {
  721. if (!_extractedParams) {
  722. MakeParamsCopy(_userProvidedParams);
  723. _extractedParams = true;
  724. }
  725. return Ast.Call(
  726. typeof(PythonOps).GetMethod("ExtractParamsArgument"),
  727. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function
  728. AstUtils.Constant(Signature.ArgumentCount), // arg count
  729. _params // list
  730. );
  731. }
  732. private static Expression VariableOrNull(ParameterExpression var, Type type) {
  733. if (var != null) {
  734. return AstUtils.Convert(
  735. var,
  736. type
  737. );
  738. }
  739. return AstUtils.Constant(null, type);
  740. }
  741. /// <summary>
  742. /// Fixes up the argument list for the appropriate target delegate type.
  743. /// </summary>
  744. private Expression/*!*/[]/*!*/ GetArgumentsForTargetType(Expression[] exprArgs) {
  745. Type target = _func.Value.func_code.Target.GetType();
  746. if (target == typeof(Func<PythonFunction, object[], object>)) {
  747. exprArgs = new Expression[] {
  748. AstUtils.NewArrayHelper(typeof(object), exprArgs)
  749. };
  750. }
  751. return exprArgs;
  752. }
  753. /// <summary>
  754. /// Helper function to get the function argument strongly typed.
  755. /// </summary>
  756. private UnaryExpression/*!*/ GetFunctionParam() {
  757. return Ast.Convert(_func.Expression, typeof(PythonFunction));
  758. }
  759. /// <summary>
  760. /// Called when the user is expanding a dictionary - we copy the user
  761. /// dictionary and verify that it contains only valid string names.
  762. /// </summary>
  763. private DynamicMetaObject/*!*/ MakeDictionaryCopy(DynamicMetaObject/*!*/ userDict) {
  764. Debug.Assert(_dict == null);
  765. userDict = userDict.Restrict(userDict.GetLimitType());
  766. _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict"));
  767. EnsureInit();
  768. string methodName;
  769. if (typeof(PythonDictionary).IsAssignableFrom(userDict.GetLimitType())) {
  770. methodName = "CopyAndVerifyPythonDictionary";
  771. } else if (typeof(IDictionary).IsAssignableFrom(userDict.GetLimitType())) {
  772. methodName = "CopyAndVerifyDictionary";
  773. } else {
  774. methodName = "CopyAndVerifyUserMapping";
  775. }
  776. _init.Add(
  777. Ast.Assign(
  778. _dict,
  779. Ast.Call(
  780. typeof(PythonOps).GetMethod(methodName),
  781. GetFunctionParam(),
  782. AstUtils.Convert(userDict.Expression, userDict.GetLimitType())
  783. )
  784. )
  785. );
  786. return userDict;
  787. }
  788. /// <summary>
  789. /// Called when the user is expanding a params argument
  790. /// </summary>
  791. private void MakeParamsCopy(Expression/*!*/ userList) {
  792. Debug.Assert(_params == null);
  793. _temps.Add(_params = Ast.Variable(typeof(List), "$list"));
  794. _temps.Add(_paramsLen = Ast.Variable(typeof(int), "$paramsLen"));
  795. EnsureInit();
  796. _init.Add(
  797. Ast.Assign(
  798. _params,
  799. Ast.Call(
  800. typeof(PythonOps).GetMethod("CopyAndVerifyParamsList"),
  801. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  802. AstUtils.Convert(userList, typeof(object))
  803. )
  804. )
  805. );
  806. _init.Add(
  807. Ast.Assign(_paramsLen,
  808. Ast.Add(
  809. Ast.Call(_params, typeof(List).GetMethod("__len__")),
  810. AstUtils.Constant(Signature.GetProvidedPositionalArgumentCount())
  811. )
  812. )
  813. );
  814. }
  815. /// <summary>
  816. /// Called when the user hasn't supplied a dictionary to be expanded but the
  817. /// function takes a dictionary to be expanded.
  818. /// </summary>
  819. private Expression MakeDictionary(Dictionary<string, Expression/*!*/> namedArgs) {
  820. Debug.Assert(_dict == null);
  821. _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict"));
  822. Expression dictCreator;
  823. if (namedArgs != null) {
  824. Debug.Assert(namedArgs.Count > 0);
  825. Expression[] items = new Expression[namedArgs.Count * 2];
  826. int itemIndex = 0;
  827. foreach (KeyValuePair<string, Expression> kvp in namedArgs) {
  828. items[itemIndex++] = AstUtils.Convert(kvp.Value, typeof(object));
  829. items[itemIndex++] = AstUtils.Constant(kvp.Key, typeof(object));
  830. }
  831. dictCreator = Ast.Assign(
  832. _dict,
  833. Ast.Call(
  834. typeof(PythonOps).GetMethod("MakeHomogeneousDictFromItems"),
  835. Ast.NewArrayInit(typeof(object), items)
  836. )
  837. );
  838. } else {
  839. dictCreator = Ast.Assign(
  840. _dict,
  841. Ast.Call(
  842. typeof(PythonOps).GetMethod("MakeDict"),
  843. AstUtils.Constant(0)
  844. )
  845. );
  846. }
  847. return dictCreator;
  848. }
  849. /// <summary>
  850. /// Helper function to create the expression for creating the actual tuple passed through.
  851. /// </summary>
  852. private static Expression/*!*/ MakeParamsTuple(List<Expression> extraArgs) {
  853. if (extraArgs != null) {
  854. return AstUtils.ComplexCallHelper(
  855. typeof(PythonOps).GetMethod("MakeTuple"),
  856. extraArgs.ToArray()
  857. );
  858. }
  859. return Ast.Call(
  860. typeof(PythonOps).GetMethod("MakeTuple"),
  861. Ast.NewArrayInit(typeof(object[]))
  862. );
  863. }
  864. /// <summary>
  865. /// Creates the code to invoke the target delegate function w/ the specified arguments.
  866. /// </summary>
  867. private Expression/*!*/ MakeFunctionInvoke(Expression[] invokeArgs) {
  868. Type targetType = _func.Value.func_code.Target.GetType();
  869. MethodInfo method = targetType.GetMethod("Invoke");
  870. // If calling generator, create the instance of PythonGenerator first
  871. // and add it into the list of arguments
  872. invokeArgs = ArrayUtils.Insert(GetFunctionParam(), invokeArgs);
  873. Expression invoke = AstUtils.SimpleCallHelper(
  874. Ast.Convert(
  875. Ast.Call(
  876. _call.SupportsLightThrow() ?
  877. typeof(PythonOps).GetMethod("FunctionGetLightThrowTarget")
  878. : typeof(PythonOps).GetMethod("FunctionGetTarget"),
  879. GetFunctionParam()
  880. ),
  881. targetType
  882. ),
  883. method,
  884. invokeArgs
  885. );
  886. if (_paramlessCheck != null) {
  887. invoke = Expression.Block(_paramlessCheck, invoke);
  888. }
  889. return invoke;
  890. }
  891. /// <summary>
  892. /// Appends the initialization code for the call to the function if any exists.
  893. /// </summary>
  894. private Expression/*!*/ AddInitialization(Expression body) {
  895. if (_init == null) return body;
  896. List<Expression> res = new List<Expression>(_init);
  897. res.Add(body);
  898. return Ast.Block(res);
  899. }
  900. private void MakeUnexpectedKeywordError(Dictionary<string, Expression> namedArgs) {
  901. string name = null;
  902. foreach (string id in namedArgs.Keys) {
  903. name = id;
  904. break;
  905. }
  906. _error = _call.Throw(
  907. Ast.Call(
  908. typeof(PythonOps).GetMethod("UnexpectedKeywordArgumentError"),
  909. AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)),
  910. AstUtils.Constant(name, typeof(string))
  911. ),
  912. typeof(PythonOps)
  913. );
  914. }
  915. private void EnsureInit() {
  916. if (_init == null) _init = new List<Expression>();
  917. }
  918. }
  919. #endregion
  920. #region Operations
  921. private static DynamicMetaObject/*!*/ MakeCallSignatureRule(DynamicMetaObject self) {
  922. return new DynamicMetaObject(
  923. Ast.Call(
  924. typeof(PythonOps).GetMethod("GetFunctionSignature"),
  925. AstUtils.Convert(
  926. self.Expression,
  927. typeof(PythonFunction)
  928. )
  929. ),
  930. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction))
  931. );
  932. }
  933. private static DynamicMetaObject MakeIsCallableRule(DynamicMetaObject/*!*/ self) {
  934. return new DynamicMetaObject(
  935. AstUtils.Constant(true),
  936. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction))
  937. );
  938. }
  939. #endregion
  940. #region Helpers
  941. public new PythonFunction/*!*/ Value {
  942. get {
  943. return (PythonFunction)base.Value;
  944. }
  945. }
  946. #endregion
  947. #region IPythonOperable Members
  948. DynamicMetaObject IPythonOperable.BindOperation(PythonOperationBinder action, DynamicMetaObject[] args) {
  949. switch (action.Operation) {
  950. case PythonOperationKind.CallSignatures:
  951. return MakeCallSignatureRule(this);
  952. case PythonOperationKind.IsCal

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