PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/DICK.B1/IronPython/Runtime/PythonFunction.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 603 lines | 427 code | 101 blank | 75 comment | 63 complexity | 0b79d49063af310f73fa992e6e46f809 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 !CLR2
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Dynamic;
  24. using System.Runtime.CompilerServices;
  25. using System.Text;
  26. using System.Threading;
  27. using Microsoft.Scripting;
  28. using Microsoft.Scripting.Runtime;
  29. using Microsoft.Scripting.Utils;
  30. using IronPython.Compiler;
  31. using IronPython.Runtime.Operations;
  32. using IronPython.Runtime.Types;
  33. namespace IronPython.Runtime {
  34. using Ast = Expression;
  35. using SpecialNameAttribute = System.Runtime.CompilerServices.SpecialNameAttribute;
  36. /// <summary>
  37. /// Created for a user-defined function.
  38. /// </summary>
  39. [PythonType("function"), DontMapGetMemberNamesToDir, DebuggerDisplay("function {__name__} in {__module__}")]
  40. public sealed partial class PythonFunction : PythonTypeSlot, IWeakReferenceable, IPythonMembersList, IDynamicMetaObjectProvider, ICodeFormattable, Binding.IFastInvokable {
  41. private readonly CodeContext/*!*/ _context; // the creating code context of the function
  42. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
  43. [PythonHidden]
  44. public readonly MutableTuple Closure;
  45. private object[]/*!*/ _defaults; // the default parameters of the method
  46. internal PythonDictionary _dict; // a dictionary to story arbitrary members on the function object
  47. private object _module; // the module name
  48. private int _id, _compat; // ID/Compat flags used for testing in rules
  49. private FunctionCode _code; // the Python function code object. Not currently used for much by us...
  50. private string _name; // the name of the method
  51. private object _doc; // the current documentation string
  52. private static int[] _depth_fast = new int[20]; // hi-perf thread static data to avoid hitting a real thread static
  53. [ThreadStatic] private static int DepthSlow; // current depth stored in a real thread static with fast depth runs out
  54. [MultiRuntimeAware]
  55. private static int _CurrentId = 1; // The current ID for functions which are called in complex ways.
  56. /// <summary>
  57. /// Python ctor - maps to function.__new__
  58. ///
  59. /// y = func(x.__code__, globals(), 'foo', None, (a, ))
  60. /// </summary>
  61. public PythonFunction(CodeContext context, FunctionCode code, PythonDictionary globals, string name, PythonTuple defaults, PythonTuple closure) {
  62. throw new NotImplementedException();
  63. }
  64. internal PythonFunction(CodeContext/*!*/ context, FunctionCode funcInfo, object modName, object[] defaults, MutableTuple closure) {
  65. Assert.NotNull(context, funcInfo);
  66. _context = context;
  67. _defaults = defaults ?? ArrayUtils.EmptyObjects;
  68. _code = funcInfo;
  69. _doc = funcInfo._initialDoc;
  70. _name = funcInfo.co_name;
  71. Debug.Assert(_defaults.Length <= _code.co_argcount);
  72. if (modName != Uninitialized.Instance) {
  73. _module = modName;
  74. }
  75. Closure = closure;
  76. _compat = CalculatedCachedCompat();
  77. }
  78. #region Public APIs
  79. public object __globals__ {
  80. get {
  81. return func_globals;
  82. }
  83. set {
  84. throw PythonOps.TypeError("readonly attribute");
  85. }
  86. }
  87. public object func_globals {
  88. get {
  89. return _context.GlobalDict;
  90. }
  91. set {
  92. throw PythonOps.TypeError("readonly attribute");
  93. }
  94. }
  95. [PropertyMethod, SpecialName, System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  96. public void Deletefunc_globals() {
  97. throw PythonOps.TypeError("readonly attribute");
  98. }
  99. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  100. public PythonTuple __defaults__ {
  101. get {
  102. return func_defaults;
  103. }
  104. set {
  105. func_defaults = value;
  106. }
  107. }
  108. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  109. public PythonTuple func_defaults {
  110. get {
  111. if (_defaults.Length == 0) return null;
  112. return new PythonTuple(_defaults);
  113. }
  114. set {
  115. if (value == null) {
  116. _defaults = ArrayUtils.EmptyObjects;
  117. } else {
  118. _defaults = value.ToArray();
  119. }
  120. _compat = CalculatedCachedCompat();
  121. }
  122. }
  123. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  124. public PythonTuple __closure__ {
  125. get {
  126. return func_closure;
  127. }
  128. set {
  129. func_closure = value;
  130. }
  131. }
  132. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  133. public PythonTuple func_closure {
  134. get {
  135. var storage = (Context.Dict._storage as RuntimeVariablesDictionaryStorage);
  136. if (storage != null) {
  137. object[] res = new object[storage.Names.Length];
  138. for (int i = 0; i < res.Length; i++) {
  139. res[i] = storage.GetCell(i);
  140. }
  141. return PythonTuple.MakeTuple(res);
  142. }
  143. return null;
  144. }
  145. set {
  146. throw PythonOps.TypeError("readonly attribute");
  147. }
  148. }
  149. public string __name__ {
  150. get { return func_name; }
  151. set { func_name = value; }
  152. }
  153. public string func_name {
  154. get { return _name; }
  155. set {
  156. if (value == null) {
  157. throw PythonOps.TypeError("func_name must be set to a string object");
  158. }
  159. _name = value;
  160. }
  161. }
  162. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  163. public PythonDictionary __dict__ {
  164. get { return func_dict; }
  165. set { func_dict = value; }
  166. }
  167. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  168. public PythonDictionary/*!*/ func_dict {
  169. get { return EnsureDict(); }
  170. set {
  171. if (value == null) throw PythonOps.TypeError("setting function's dictionary to non-dict");
  172. _dict = value;
  173. }
  174. }
  175. public object __doc__ {
  176. get { return _doc; }
  177. set { _doc = value; }
  178. }
  179. public object func_doc {
  180. get { return __doc__; }
  181. set { __doc__ = value; }
  182. }
  183. public object __module__ {
  184. get { return _module; }
  185. set { _module = value; }
  186. }
  187. public FunctionCode __code__ {
  188. get {
  189. return func_code;
  190. }
  191. set {
  192. func_code = value;
  193. }
  194. }
  195. public FunctionCode func_code {
  196. get {
  197. return _code;
  198. }
  199. set {
  200. if (value == null) throw PythonOps.TypeError("func_code must be set to a code object");
  201. _code = value;
  202. }
  203. }
  204. public object __call__(CodeContext/*!*/ context, params object[] args) {
  205. return PythonCalls.Call(context, this, args);
  206. }
  207. public object __call__(CodeContext/*!*/ context, [ParamDictionary]IDictionary<object, object> dict, params object[] args) {
  208. return PythonCalls.CallWithKeywordArgs(context, this, args, dict);
  209. }
  210. #endregion
  211. #region Internal APIs
  212. internal SourceSpan Span {
  213. get { return func_code.Span; }
  214. }
  215. internal string[] ArgNames {
  216. get { return func_code.ArgNames; }
  217. }
  218. /// <summary>
  219. /// The parent CodeContext in which this function was declared.
  220. /// </summary>
  221. internal CodeContext Context {
  222. get {
  223. return _context;
  224. }
  225. }
  226. internal string GetSignatureString() {
  227. StringBuilder sb = new StringBuilder(__name__);
  228. sb.Append('(');
  229. for (int i = 0; i < _code.ArgNames.Length; i++) {
  230. if (i != 0) sb.Append(", ");
  231. if (i == ExpandDictPosition) {
  232. sb.Append("**");
  233. } else if (i == ExpandListPosition) {
  234. sb.Append("*");
  235. }
  236. sb.Append(ArgNames[i]);
  237. if (i < NormalArgumentCount) {
  238. int noDefaults = NormalArgumentCount - Defaults.Length; // number of args w/o defaults
  239. if (i - noDefaults >= 0) {
  240. sb.Append('=');
  241. sb.Append(PythonOps.Repr(Context, Defaults[i - noDefaults]));
  242. }
  243. }
  244. }
  245. sb.Append(')');
  246. return sb.ToString();
  247. }
  248. /// <summary>
  249. /// Captures the # of args and whether we have kw / arg lists. This
  250. /// enables us to share sites for simple calls (calls that don't directly
  251. /// provide named arguments or the list/dict params).
  252. /// </summary>
  253. internal int FunctionCompatibility {
  254. get {
  255. return _compat;
  256. }
  257. }
  258. /// <summary>
  259. /// Calculates the _compat value which is used for call-compatibility checks
  260. /// for simple calls. Whenver any of the dependent values are updated this
  261. /// must be called again.
  262. ///
  263. /// The dependent values include:
  264. /// _nparams - this is readonly, and never requies an update
  265. /// _defaults - the user can mutate this (func_defaults) and that forces
  266. /// an update
  267. /// expand dict/list - based on nparams and flags, both read-only
  268. ///
  269. /// Bits are allocated as:
  270. /// 00003fff - Normal argument count
  271. /// 0fffb000 - Default count
  272. /// 10000000 - unused
  273. /// 20000000 - expand list
  274. /// 40000000 - expand dict
  275. /// 80000000 - unused
  276. ///
  277. /// Enforce recursion is added at runtime.
  278. /// </summary>
  279. private int CalculatedCachedCompat() {
  280. return NormalArgumentCount |
  281. Defaults.Length << 14 |
  282. ((ExpandDictPosition != -1) ? 0x40000000 : 0) |
  283. ((ExpandListPosition != -1) ? 0x20000000 : 0);
  284. }
  285. /// <summary>
  286. /// Generators w/ exception handling need to have some data stored
  287. /// on them so that we appropriately set/restore the exception state.
  288. /// </summary>
  289. internal bool IsGeneratorWithExceptionHandling {
  290. get {
  291. return ((_code.Flags & (FunctionAttributes.CanSetSysExcInfo | FunctionAttributes.Generator)) == (FunctionAttributes.CanSetSysExcInfo | FunctionAttributes.Generator));
  292. }
  293. }
  294. /// <summary>
  295. /// Returns an ID for the function if one has been assigned, or zero if the
  296. /// function has not yet required the use of an ID.
  297. /// </summary>
  298. internal int FunctionID {
  299. get {
  300. return _id;
  301. }
  302. }
  303. /// <summary>
  304. /// Gets the position for the expand list argument or -1 if the function doesn't have an expand list parameter.
  305. /// </summary>
  306. internal int ExpandListPosition {
  307. get {
  308. if ((_code.Flags & FunctionAttributes.ArgumentList) != 0) {
  309. return _code.co_argcount;
  310. }
  311. return -1;
  312. }
  313. }
  314. /// <summary>
  315. /// Gets the position for the expand dictionary argument or -1 if the function doesn't have an expand dictionary parameter.
  316. /// </summary>
  317. internal int ExpandDictPosition {
  318. get {
  319. if ((_code.Flags & FunctionAttributes.KeywordDictionary) != 0) {
  320. if ((_code.Flags & FunctionAttributes.ArgumentList) != 0) {
  321. return _code.co_argcount + 1;
  322. }
  323. return _code.co_argcount;
  324. }
  325. return -1;
  326. }
  327. }
  328. /// <summary>
  329. /// Gets the number of normal (not params or kw-params) parameters.
  330. /// </summary>
  331. internal int NormalArgumentCount {
  332. get {
  333. return _code.co_argcount;
  334. }
  335. }
  336. /// <summary>
  337. /// Gets the number of extra arguments (params or kw-params)
  338. /// </summary>
  339. internal int ExtraArguments {
  340. get {
  341. if ((_code.Flags & FunctionAttributes.ArgumentList) != 0) {
  342. if ((_code.Flags & FunctionAttributes.KeywordDictionary) != 0) {
  343. return 2;
  344. }
  345. return 1;
  346. } else if ((_code.Flags & FunctionAttributes.KeywordDictionary) != 0) {
  347. return 1;
  348. }
  349. return 0;
  350. }
  351. }
  352. internal FunctionAttributes Flags {
  353. get {
  354. return _code.Flags;
  355. }
  356. }
  357. internal object[] Defaults {
  358. get { return _defaults; }
  359. }
  360. internal Exception BadArgumentError(int count) {
  361. return BinderOps.TypeErrorForIncorrectArgumentCount(__name__, NormalArgumentCount, Defaults.Length, count, ExpandListPosition != -1, false);
  362. }
  363. internal Exception BadKeywordArgumentError(int count) {
  364. return BinderOps.TypeErrorForIncorrectArgumentCount(__name__, NormalArgumentCount, Defaults.Length, count, ExpandListPosition != -1, true);
  365. }
  366. #endregion
  367. #region Custom member lookup operators
  368. IList<string> IMembersList.GetMemberNames() {
  369. return PythonOps.GetStringMemberList(this);
  370. }
  371. IList<object> IPythonMembersList.GetMemberNames(CodeContext/*!*/ context) {
  372. List list;
  373. if (_dict == null) {
  374. list = PythonOps.MakeList();
  375. } else {
  376. list = PythonOps.MakeListFromSequence(_dict);
  377. }
  378. list.AddNoLock("__module__");
  379. list.extend(TypeCache.Function.GetMemberNames(context, this));
  380. return list;
  381. }
  382. #endregion
  383. #region IWeakReferenceable Members
  384. WeakRefTracker IWeakReferenceable.GetWeakRef() {
  385. if (_dict != null) {
  386. object weakRef;
  387. if (_dict.TryGetValue("__weakref__", out weakRef)) {
  388. return weakRef as WeakRefTracker;
  389. }
  390. }
  391. return null;
  392. }
  393. bool IWeakReferenceable.SetWeakRef(WeakRefTracker value) {
  394. EnsureDict();
  395. _dict["__weakref__"] = value;
  396. return true;
  397. }
  398. void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
  399. ((IWeakReferenceable)this).SetWeakRef(value);
  400. }
  401. #endregion
  402. #region Private APIs
  403. internal PythonDictionary EnsureDict() {
  404. if (_dict == null) {
  405. Interlocked.CompareExchange(ref _dict, PythonDictionary.MakeSymbolDictionary(), null);
  406. }
  407. return _dict;
  408. }
  409. internal static int AddRecursionDepth(int change) {
  410. // ManagedThreadId starts at 1 and increases as we get more threads.
  411. // Therefore we keep track of a limited number of threads in an array
  412. // that only gets created once, and we access each of the elements
  413. // from only a single thread.
  414. uint tid = (uint)Thread.CurrentThread.ManagedThreadId;
  415. if (tid < _depth_fast.Length) {
  416. return _depth_fast[tid] += change;
  417. } else {
  418. return DepthSlow += change;
  419. }
  420. }
  421. internal void EnsureID() {
  422. if (_id == 0) {
  423. Interlocked.CompareExchange(ref _id, Interlocked.Increment(ref _CurrentId), 0);
  424. }
  425. }
  426. #endregion
  427. #region PythonTypeSlot Overrides
  428. internal override bool TryGetValue(CodeContext context, object instance, PythonType owner, out object value) {
  429. value = new Method(this, instance, owner);
  430. return true;
  431. }
  432. internal override bool GetAlwaysSucceeds {
  433. get {
  434. return true;
  435. }
  436. }
  437. #endregion
  438. #region ICodeFormattable Members
  439. public string/*!*/ __repr__(CodeContext/*!*/ context) {
  440. return string.Format("<function {0} at {1}>", func_name, PythonOps.HexId(this));
  441. }
  442. #endregion
  443. #region IDynamicMetaObjectProvider Members
  444. DynamicMetaObject/*!*/ IDynamicMetaObjectProvider.GetMetaObject(Expression/*!*/ parameter) {
  445. return new Binding.MetaPythonFunction(parameter, BindingRestrictions.Empty, this);
  446. }
  447. #endregion
  448. }
  449. [PythonType("cell")]
  450. public sealed class ClosureCell : ICodeFormattable
  451. #if CLR2
  452. , IValueEquality
  453. #endif
  454. {
  455. [PythonHidden]
  456. public object Value;
  457. internal ClosureCell(object value) {
  458. Value = value;
  459. }
  460. public object cell_contents {
  461. get {
  462. if (Value == Uninitialized.Instance) {
  463. throw PythonOps.ValueError("cell is empty");
  464. }
  465. return Value;
  466. }
  467. }
  468. #region ICodeFormattable Members
  469. public string/*!*/ __repr__(CodeContext/*!*/ context) {
  470. return String.Format("<cell at {0}: {1}>",
  471. IdDispenser.GetId(this),
  472. GetContentsRepr()
  473. );
  474. }
  475. private string GetContentsRepr() {
  476. if (Value == Uninitialized.Instance) {
  477. return "empty";
  478. }
  479. return String.Format("{0} object at {1}",
  480. PythonTypeOps.GetName(Value),
  481. IdDispenser.GetId(Value));
  482. }
  483. #endregion
  484. #region IValueEquality Members
  485. public const object __hash__ = null;
  486. #if CLR2
  487. int IValueEquality.GetValueHashCode() {
  488. throw PythonOps.TypeError("unhashable type: cell");
  489. }
  490. bool IValueEquality.ValueEquals(object other) {
  491. return __cmp__(other) == 0;
  492. }
  493. #endif
  494. #endregion
  495. [Python3Warning("cell comparisons not supported in 3.x")]
  496. public int __cmp__(object other) {
  497. ClosureCell cc = other as ClosureCell;
  498. if (cc == null) throw PythonOps.TypeError("cell.__cmp__(x,y) expected cell, got {0}", PythonTypeOps.GetName(other));
  499. return PythonOps.Compare(Value, cc.Value);
  500. }
  501. }
  502. }