PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython/Runtime/PythonFunction.cs

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