PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/FunctionCode.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 1136 lines | 753 code | 168 blank | 215 comment | 110 complexity | 345a6fe503cfbd24b4f5367cc53a12e8 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.Generic;
  22. using System.Diagnostics;
  23. using System.Runtime.CompilerServices;
  24. using System.Threading;
  25. using Microsoft.Scripting;
  26. using Microsoft.Scripting.Generation;
  27. using Microsoft.Scripting.Interpreter;
  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. /// <summary>
  35. /// Represents a piece of code. This can reference either a CompiledCode
  36. /// object or a Function. The user can explicitly call FunctionCode by
  37. /// passing it into exec or eval.
  38. /// </summary>
  39. [PythonType("code")]
  40. public class FunctionCode : IExpressionSerializable {
  41. [PythonHidden]
  42. public Delegate Target; // the current target for the function. This can change based upon adaptive compilation, recursion enforcement, and tracing.
  43. private Delegate _normalDelegate; // the normal delegate - this can be a compiled or interpreted delegate.
  44. private Compiler.Ast.ScopeStatement _lambda; // the original DLR lambda that contains the code
  45. internal readonly string _initialDoc; // the initial doc string
  46. private readonly int _localCount; // the number of local variables in the code
  47. private readonly int _argCount; // cached locally because it's used during calls w/ defaults
  48. // debugging/tracing support
  49. private LambdaExpression _tracingLambda; // the transformed lambda used for tracing/debugging
  50. private Delegate _tracingDelegate; // the delegate used for tracing/debugging, if one has been created. This can be interpreted or compiled.
  51. /// <summary>
  52. /// This is both the lock that is held while enumerating the threads or updating the thread accounting
  53. /// information. It's also a marker CodeList which is put in place when we are enumerating the thread
  54. /// list and all additions need to block.
  55. ///
  56. /// This lock is also acquired whenever we need to calculate how a function's delegate should be created
  57. /// so that we don't race against sys.settrace/sys.setprofile.
  58. /// </summary>
  59. private static CodeList _CodeCreateAndUpdateDelegateLock = new CodeList();
  60. /// <summary>
  61. /// Constructor used to create a FunctionCode for code that's been serialized to disk.
  62. ///
  63. /// Code constructed this way cannot be interpreted or debugged using sys.settrace/sys.setprofile.
  64. ///
  65. /// Function codes created this way do support recursion enforcement and are therefore registered in the global function code registry.
  66. /// </summary>
  67. internal FunctionCode(PythonContext context, Delegate code, Compiler.Ast.ScopeStatement scope, string documentation, int localCount) {
  68. _normalDelegate = code;
  69. _lambda = scope;
  70. _argCount = CalculateArgumentCount();
  71. _initialDoc = documentation;
  72. // need to take this lock to ensure sys.settrace/sys.setprofile is not actively changing
  73. lock (_CodeCreateAndUpdateDelegateLock) {
  74. Target = AddRecursionCheck(context, code);
  75. }
  76. RegisterFunctionCode(context);
  77. }
  78. /// <summary>
  79. /// Constructor to create a FunctionCode at runtime.
  80. ///
  81. /// Code constructed this way supports both being interpreted and debugged. When necessary the code will
  82. /// be re-compiled or re-interpreted for that specific purpose.
  83. ///
  84. /// Function codes created this way do support recursion enforcement and are therefore registered in the global function code registry.
  85. ///
  86. /// the initial delegate provided here should NOT be the actual code. It should always be a delegate which updates our Target lazily.
  87. /// </summary>
  88. internal FunctionCode(PythonContext context, Delegate initialDelegate, Compiler.Ast.ScopeStatement scope, string documentation) {
  89. _lambda = scope;
  90. Target = initialDelegate;
  91. _initialDoc = documentation;
  92. _localCount = scope.Variables == null ? 0 : scope.Variables.Count;
  93. _argCount = CalculateArgumentCount();
  94. RegisterFunctionCode(context);
  95. }
  96. private static PythonTuple SymbolListToTuple(IList<string> vars) {
  97. if (vars != null && vars.Count != 0) {
  98. object[] tupleData = new object[vars.Count];
  99. for (int i = 0; i < vars.Count; i++) {
  100. tupleData[i] = vars[i];
  101. }
  102. return PythonTuple.MakeTuple(tupleData);
  103. } else {
  104. return PythonTuple.EMPTY;
  105. }
  106. }
  107. private static PythonTuple StringArrayToTuple(string[] closureVars) {
  108. if (closureVars != null && closureVars.Length != 0) {
  109. return PythonTuple.MakeTuple((object[])closureVars);
  110. } else {
  111. return PythonTuple.EMPTY;
  112. }
  113. }
  114. /// <summary>
  115. /// Registers the current function code in our global weak list of all function codes.
  116. ///
  117. /// The weak list can be enumerated with GetAllCode().
  118. ///
  119. /// Ultimately there are 3 types of threads we care about races with:
  120. /// 1. Other threads which are registering function codes
  121. /// 2. Threads calling sys.settrace which require the world to stop and get updated
  122. /// 3. Threads running cleanup (thread pool thread, or call to gc.collect).
  123. ///
  124. /// The 1st two must have perfect synchronization. We cannot have a thread registering
  125. /// a new function which another thread is trying to update all of the functions in the world. Doing
  126. /// so would mean we could miss adding tracing to a thread.
  127. ///
  128. /// But the cleanup thread can run in parallel to either registrying or sys.settrace. The only
  129. /// thing it needs to take a lock for is updating our accounting information about the
  130. /// number of code objects are alive.
  131. /// </summary>
  132. private void RegisterFunctionCode(PythonContext context) {
  133. if (_lambda == null) {
  134. return;
  135. }
  136. WeakReference codeRef = new WeakReference(this, true);
  137. CodeList prevCode;
  138. lock (_CodeCreateAndUpdateDelegateLock) {
  139. Debug.Assert(context._allCodes != _CodeCreateAndUpdateDelegateLock);
  140. // we do an interlocked operation here because this can run in parallel w/ the CodeCleanup thread. The
  141. // lock only prevents us from running in parallel w/ an update to all of the functions in the world which
  142. // needs to be synchronized.
  143. do {
  144. prevCode = context._allCodes;
  145. } while (Interlocked.CompareExchange(ref context._allCodes, new CodeList(codeRef, prevCode), prevCode) != prevCode);
  146. if (context._codeCount++ == context._nextCodeCleanup) {
  147. // run cleanup of codes on another thread
  148. CleanFunctionCodes(context, false);
  149. }
  150. }
  151. }
  152. internal static void CleanFunctionCodes(PythonContext context, bool synchronous) {
  153. if (synchronous) {
  154. CodeCleanup(context);
  155. } else {
  156. ThreadPool.QueueUserWorkItem(CodeCleanup, context);
  157. }
  158. }
  159. /// <summary>
  160. /// Enumerates all function codes for updating the current type of targets we generate.
  161. ///
  162. /// While enumerating we hold a lock so that users cannot change sys.settrace/sys.setprofile
  163. /// until the lock is released.
  164. /// </summary>
  165. private static IEnumerable<FunctionCode> GetAllCode(PythonContext context) {
  166. // only a single thread can enumerate the current FunctionCodes at a time.
  167. lock (_CodeCreateAndUpdateDelegateLock) {
  168. CodeList curCodeList = Interlocked.Exchange(ref context._allCodes, _CodeCreateAndUpdateDelegateLock);
  169. Debug.Assert(curCodeList != _CodeCreateAndUpdateDelegateLock);
  170. CodeList initialCode = curCodeList;
  171. try {
  172. while (curCodeList != null) {
  173. WeakReference codeRef = curCodeList.Code;
  174. FunctionCode target = (FunctionCode)codeRef.Target;
  175. if (target != null) {
  176. yield return target;
  177. }
  178. curCodeList = curCodeList.Next;
  179. }
  180. } finally {
  181. Interlocked.Exchange(ref context._allCodes, initialCode);
  182. }
  183. }
  184. }
  185. internal static void UpdateAllCode(PythonContext context) {
  186. foreach (FunctionCode fc in GetAllCode(context)) {
  187. fc.UpdateDelegate(context, false);
  188. }
  189. }
  190. private static void CodeCleanup(object state) {
  191. PythonContext context = (PythonContext)state;
  192. // only allow 1 thread at a time to do cleanup (other threads can continue adding)
  193. lock (context._codeCleanupLock) {
  194. // the bulk of the work is in scanning the list, this proceeeds lock free
  195. int removed = 0, kept = 0, origCount = context._codeCount;
  196. CodeList prev = null;
  197. CodeList cur = GetRootCodeNoUpdating(context);
  198. while (cur != null) {
  199. if (!cur.Code.IsAlive) {
  200. if (prev == null) {
  201. if (Interlocked.CompareExchange(ref context._allCodes, cur.Next, cur) != cur) {
  202. // someone else snuck in and added a new code entry, spin and try again.
  203. cur = GetRootCodeNoUpdating(context);
  204. continue;
  205. }
  206. cur = cur.Next;
  207. removed++;
  208. continue;
  209. } else {
  210. // remove from the linked list, we're the only one who can change this.
  211. Debug.Assert(prev.Next == cur);
  212. removed++;
  213. cur = prev.Next = cur.Next;
  214. continue;
  215. }
  216. } else {
  217. kept++;
  218. }
  219. prev = cur;
  220. cur = cur.Next;
  221. }
  222. // finally update our bookkeeping statistics which requires locking but is fast.
  223. lock (_CodeCreateAndUpdateDelegateLock) {
  224. // calculate the next cleanup, we want each pass to remove ~50% of all entries
  225. const double removalGoal = .50;
  226. if (context._codeCount == 0) {
  227. // somehow we would have had to queue a bunch of function codes, have 1 thread
  228. // clean them up all up, and a 2nd queued thread waiting to clean them up as well.
  229. // At the same time there would have to be no live functions defined which means
  230. // we're executing top-level code which is causing this to happen.
  231. context._nextCodeCleanup = 200;
  232. return;
  233. }
  234. //Console.WriteLine("Removed {0} entries, {1} remain", removed, context._codeCount);
  235. Debug.Assert(removed <= context._codeCount);
  236. double pctRemoved = (double)removed / (double)context._codeCount; // % of code removed
  237. double targetRatio = pctRemoved / removalGoal; // how we need to adjust our last goal
  238. // update the total and next node cleanup
  239. int newCount = Interlocked.Add(ref context._codeCount, -removed);
  240. Debug.Assert(newCount >= 0);
  241. // set the new target for cleanup
  242. int nextCleanup = targetRatio != 0 ? newCount + (int)(context._nextCodeCleanup / targetRatio) : -1;
  243. if (nextCleanup > 0) {
  244. // we're making good progress, use the newly calculated next cleanup point
  245. context._nextCodeCleanup = nextCleanup;
  246. } else {
  247. // none or very small amount cleaned up, just schedule a cleanup for sometime in the future.
  248. context._nextCodeCleanup += 500;
  249. }
  250. Debug.Assert(context._nextCodeCleanup >= context._codeCount, String.Format("{0} vs {1} ({2})", context._nextCodeCleanup, context._codeCount, targetRatio));
  251. }
  252. }
  253. }
  254. private static CodeList GetRootCodeNoUpdating(PythonContext context) {
  255. CodeList cur = context._allCodes;
  256. if (cur == _CodeCreateAndUpdateDelegateLock) {
  257. lock (_CodeCreateAndUpdateDelegateLock) {
  258. // wait until enumerating thread is done, but it's alright
  259. // if we got cur and then an enumeration started (because we'll
  260. // just clear entries out)
  261. cur = context._allCodes;
  262. Debug.Assert(cur != _CodeCreateAndUpdateDelegateLock);
  263. }
  264. }
  265. return cur;
  266. }
  267. internal SourceSpan Span {
  268. get {
  269. return _lambda.Span;
  270. }
  271. }
  272. internal string[] ArgNames {
  273. get {
  274. return _lambda.ParameterNames;
  275. }
  276. }
  277. internal FunctionAttributes Flags {
  278. get {
  279. return _lambda.Flags;
  280. }
  281. }
  282. internal bool IsModule {
  283. get {
  284. return _lambda is Compiler.Ast.PythonAst;
  285. }
  286. }
  287. #region Public constructors
  288. /*
  289. /// <summary>
  290. /// Standard python siganture
  291. /// </summary>
  292. /// <param name="argcount"></param>
  293. /// <param name="nlocals"></param>
  294. /// <param name="stacksize"></param>
  295. /// <param name="flags"></param>
  296. /// <param name="codestring"></param>
  297. /// <param name="constants"></param>
  298. /// <param name="names"></param>
  299. /// <param name="varnames"></param>
  300. /// <param name="filename"></param>
  301. /// <param name="name"></param>
  302. /// <param name="firstlineno"></param>
  303. /// <param name="nlotab"></param>
  304. /// <param name="freevars"></param>
  305. /// <param name="callvars"></param>
  306. public FunctionCode(int argcount, int nlocals, int stacksize, int flags, string codestring, object constants, Tuple names, Tuple varnames, string filename, string name, int firstlineno, object nlotab, [DefaultParameterValue(null)]object freevars, [DefaultParameterValue(null)]object callvars) {
  307. }*/
  308. #endregion
  309. #region Public Python API Surface
  310. public PythonTuple co_varnames {
  311. get {
  312. return SymbolListToTuple(_lambda.GetVarNames());
  313. }
  314. }
  315. public int co_argcount {
  316. get {
  317. return _argCount;
  318. }
  319. }
  320. private int CalculateArgumentCount() {
  321. int argCnt = _lambda.ArgCount;
  322. FunctionAttributes flags = Flags;
  323. if ((flags & FunctionAttributes.ArgumentList) != 0) argCnt--;
  324. if ((flags & FunctionAttributes.KeywordDictionary) != 0) argCnt--;
  325. return argCnt;
  326. }
  327. /// <summary>
  328. /// Returns a list of variable names which are accessed from nested functions.
  329. /// </summary>
  330. public PythonTuple co_cellvars {
  331. get {
  332. return SymbolListToTuple(_lambda.CellVariables != null ? ArrayUtils.ToArray(_lambda.CellVariables) : null);
  333. }
  334. }
  335. /// <summary>
  336. /// Returns the byte code. IronPython does not implement this and always
  337. /// returns an empty string for byte code.
  338. /// </summary>
  339. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  340. public object co_code {
  341. get {
  342. return String.Empty;
  343. }
  344. }
  345. /// <summary>
  346. /// Returns a list of constants used by the function.
  347. ///
  348. /// The first constant is the doc string, or None if no doc string is provided.
  349. ///
  350. /// IronPython currently does not include any other constants than the doc string.
  351. /// </summary>
  352. public PythonTuple co_consts {
  353. get {
  354. if (_initialDoc != null) {
  355. return PythonTuple.MakeTuple(_initialDoc, null);
  356. }
  357. return PythonTuple.MakeTuple((object)null);
  358. }
  359. }
  360. /// <summary>
  361. /// Returns the filename that the code object was defined in.
  362. /// </summary>
  363. public string co_filename {
  364. get {
  365. return _lambda.Filename;
  366. }
  367. }
  368. /// <summary>
  369. /// Returns the 1st line number of the code object.
  370. /// </summary>
  371. public int co_firstlineno {
  372. get {
  373. return Span.Start.Line;
  374. }
  375. }
  376. /// <summary>
  377. /// Returns a set of flags for the function.
  378. ///
  379. /// 0x04 is set if the function used *args
  380. /// 0x08 is set if the function used **args
  381. /// 0x20 is set if the function is a generator
  382. /// </summary>
  383. public int co_flags {
  384. get {
  385. return (int)Flags;
  386. }
  387. }
  388. /// <summary>
  389. /// Returns a list of free variables (variables accessed
  390. /// from an outer scope). This does not include variables
  391. /// accessed in the global scope.
  392. /// </summary>
  393. public PythonTuple co_freevars {
  394. get {
  395. return SymbolListToTuple(_lambda.FreeVariables != null ? CollectionUtils.ConvertAll(_lambda.FreeVariables, x => x.Name) : null);
  396. }
  397. }
  398. /// <summary>
  399. /// Returns a mapping between byte code and line numbers. IronPython does
  400. /// not implement this because byte code is not available.
  401. /// </summary>
  402. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  403. public object co_lnotab {
  404. get {
  405. throw PythonOps.NotImplementedError("");
  406. }
  407. }
  408. /// <summary>
  409. /// Returns the name of the code (function name, class name, or &lt;module&gt;).
  410. /// </summary>
  411. public string co_name {
  412. get {
  413. return _lambda.Name;
  414. }
  415. }
  416. /// <summary>
  417. /// Returns a list of global variable names accessed by the code.
  418. /// </summary>
  419. public PythonTuple co_names {
  420. get {
  421. return SymbolListToTuple(_lambda.GlobalVariables);
  422. }
  423. }
  424. /// <summary>
  425. /// Returns the number of local varaibles defined in the function.
  426. /// </summary>
  427. public object co_nlocals {
  428. get {
  429. return _localCount;
  430. }
  431. }
  432. /// <summary>
  433. /// Returns the stack size. IronPython does not implement this
  434. /// because byte code is not supported.
  435. /// </summary>
  436. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  437. public object co_stacksize {
  438. get {
  439. throw PythonOps.NotImplementedError("");
  440. }
  441. }
  442. #endregion
  443. #region Internal API Surface
  444. internal LambdaExpression Code {
  445. get {
  446. return _lambda.GetLambda();
  447. }
  448. }
  449. internal Compiler.Ast.ScopeStatement PythonCode {
  450. get {
  451. return (Compiler.Ast.ScopeStatement)_lambda;
  452. }
  453. }
  454. internal object Call(CodeContext/*!*/ context) {
  455. if (co_freevars != PythonTuple.EMPTY) {
  456. throw PythonOps.TypeError("cannot exec code object that contains free variables: {0}", co_freevars.__repr__(context));
  457. }
  458. if (Target == null) {
  459. UpdateDelegate(context.LanguageContext, true);
  460. }
  461. Func<CodeContext, CodeContext> classTarget = Target as Func<CodeContext, CodeContext>;
  462. if (classTarget != null) {
  463. return classTarget(context);
  464. }
  465. Func<CodeContext, FunctionCode, object> moduleCode = Target as Func<CodeContext, FunctionCode, object>;
  466. if (moduleCode != null) {
  467. return moduleCode(context, this);
  468. }
  469. Func<FunctionCode, object> optimizedModuleCode = Target as Func<FunctionCode, object>;
  470. if (optimizedModuleCode != null) {
  471. return optimizedModuleCode(this);
  472. }
  473. var func = new PythonFunction(context, this, null, ArrayUtils.EmptyObjects, new MutableTuple<object>());
  474. CallSite<Func<CallSite, CodeContext, PythonFunction, object>> site = PythonContext.GetContext(context).FunctionCallSite;
  475. return site.Target(site, context, func);
  476. }
  477. /// <summary>
  478. /// Creates a FunctionCode object for exec/eval/execfile'd/compile'd code.
  479. ///
  480. /// The code is then executed in a specific CodeContext by calling the .Call method.
  481. /// </summary>
  482. internal static FunctionCode FromSourceUnit(SourceUnit sourceUnit, PythonCompilerOptions options) {
  483. var code = PythonContext.CompilePythonCode(sourceUnit, options, ThrowingErrorSink.Default);
  484. return ((RunnableScriptCode)code).GetFunctionCode();
  485. }
  486. #endregion
  487. #region Private helper functions
  488. private void ExpandArgsTuple(List<string> names, PythonTuple toExpand) {
  489. for (int i = 0; i < toExpand.__len__(); i++) {
  490. if (toExpand[i] is PythonTuple) {
  491. ExpandArgsTuple(names, toExpand[i] as PythonTuple);
  492. } else {
  493. names.Add(toExpand[i] as string);
  494. }
  495. }
  496. }
  497. #endregion
  498. public override bool Equals(object obj) {
  499. // overridden because CPython defines this on code objects
  500. return base.Equals(obj);
  501. }
  502. public override int GetHashCode() {
  503. // overridden because CPython defines this on code objects
  504. return base.GetHashCode();
  505. }
  506. public int __cmp__(CodeContext/*!*/ context, [NotNull]FunctionCode/*!*/ other) {
  507. if (other == this) {
  508. return 0;
  509. }
  510. long lres = IdDispenser.GetId(this) - IdDispenser.GetId(other);
  511. return lres > 0 ? 1 : -1;
  512. }
  513. // these are present in CPython but always return NotImplemented.
  514. [return: MaybeNotImplemented]
  515. [Python3Warning("code inequality comparisons not supported in 3.x")]
  516. public static NotImplementedType operator >(FunctionCode self, FunctionCode other) {
  517. return PythonOps.NotImplemented;
  518. }
  519. [return: MaybeNotImplemented]
  520. [Python3Warning("code inequality comparisons not supported in 3.x")]
  521. public static NotImplementedType operator <(FunctionCode self, FunctionCode other) {
  522. return PythonOps.NotImplemented;
  523. }
  524. [return: MaybeNotImplemented]
  525. [Python3Warning("code inequality comparisons not supported in 3.x")]
  526. public static NotImplementedType operator >=(FunctionCode self, FunctionCode other) {
  527. return PythonOps.NotImplemented;
  528. }
  529. [return: MaybeNotImplemented]
  530. [Python3Warning("code inequality comparisons not supported in 3.x")]
  531. public static NotImplementedType operator <=(FunctionCode self, FunctionCode other) {
  532. return PythonOps.NotImplemented;
  533. }
  534. /// <summary>
  535. /// Called the 1st time a function is invoked by our OriginalCallTarget* methods
  536. /// over in PythonCallTargets. This computes the real delegate which needs to be
  537. /// created for the function. Usually this means starting off interpretering. It
  538. /// also involves adding the wrapper function for recursion enforcement.
  539. ///
  540. /// Because this can race against sys.settrace/setprofile we need to take our
  541. /// _ThreadIsEnumeratingAndAccountingLock to ensure no one is actively changing all
  542. /// of the live functions.
  543. /// </summary>
  544. internal void LazyCompileFirstTarget(PythonFunction function) {
  545. lock (_CodeCreateAndUpdateDelegateLock) {
  546. UpdateDelegate(PythonContext.GetContext(function.Context), true);
  547. }
  548. }
  549. /// <summary>
  550. /// Updates the delegate based upon current Python context settings for recursion enforcement
  551. /// and for tracing.
  552. /// </summary>
  553. internal void UpdateDelegate(PythonContext context, bool forceCreation) {
  554. Delegate finalTarget;
  555. if (context.EnableTracing && _lambda != null) {
  556. if (_tracingLambda == null) {
  557. if (!forceCreation) {
  558. // the user just called sys.settrace(), don't force re-compilation of every method in the system. Instead
  559. // we'll just re-compile them as they're invoked.
  560. PythonCallTargets.GetPythonTargetType(_lambda.ParameterNames.Length > PythonCallTargets.MaxArgs, _lambda.ParameterNames.Length, out Target);
  561. return;
  562. }
  563. _tracingLambda = GetGeneratorOrNormalLambdaTracing(context);
  564. }
  565. if (_tracingDelegate == null) {
  566. _tracingDelegate = CompileLambda(_tracingLambda, new TargetUpdaterForCompilation(context, this).SetCompiledTargetTracing);
  567. }
  568. finalTarget = _tracingDelegate;
  569. } else {
  570. if (_normalDelegate == null) {
  571. if (!forceCreation) {
  572. // we cannot create the delegate when forceCreation is false because we hold the
  573. // _CodeCreateAndUpdateDelegateLock and compiling the delegate may create a FunctionCode
  574. // object which requires the lock.
  575. PythonCallTargets.GetPythonTargetType(_lambda.ParameterNames.Length > PythonCallTargets.MaxArgs, _lambda.ParameterNames.Length, out Target);
  576. return;
  577. }
  578. _normalDelegate = CompileLambda(GetGeneratorOrNormalLambda(), new TargetUpdaterForCompilation(context, this).SetCompiledTarget);
  579. }
  580. finalTarget = _normalDelegate;
  581. }
  582. finalTarget = AddRecursionCheck(context, finalTarget);
  583. Target = finalTarget;
  584. }
  585. /// <summary>
  586. /// Called to set the initial target delegate when the user has passed -X:Debug to enable
  587. /// .NET style debugging.
  588. /// </summary>
  589. internal void SetDebugTarget(PythonContext context, Delegate target) {
  590. _normalDelegate = target;
  591. Target = AddRecursionCheck(context, target);
  592. }
  593. /// <summary>
  594. /// Gets the LambdaExpression for tracing.
  595. ///
  596. /// If this is a generator function code then the lambda gets tranformed into the correct generator code.
  597. /// </summary>
  598. private LambdaExpression GetGeneratorOrNormalLambdaTracing(PythonContext context) {
  599. var debugProperties = new PythonDebuggingPayload(this);
  600. var debugInfo = new Microsoft.Scripting.Debugging.CompilerServices.DebugLambdaInfo(
  601. null, // IDebugCompilerSupport
  602. null, // lambda alias
  603. false, // optimize for leaf frames
  604. null, // hidden variables
  605. null, // variable aliases
  606. debugProperties // custom payload
  607. );
  608. if ((Flags & FunctionAttributes.Generator) == 0) {
  609. return context.DebugContext.TransformLambda((LambdaExpression)Compiler.Ast.Node.RemoveFrame(_lambda.GetLambda()), debugInfo);
  610. }
  611. return Expression.Lambda(
  612. Code.Type,
  613. new GeneratorRewriter(
  614. _lambda.Name,
  615. Compiler.Ast.Node.RemoveFrame(Code.Body)
  616. ).Reduce(
  617. _lambda.ShouldInterpret,
  618. _lambda.EmitDebugSymbols,
  619. context.Options.CompilationThreshold,
  620. Code.Parameters,
  621. x => (Expression<Func<MutableTuple, object>>)context.DebugContext.TransformLambda(x, debugInfo)
  622. ),
  623. Code.Name,
  624. Code.Parameters
  625. );
  626. }
  627. /// <summary>
  628. /// Gets the correct final LambdaExpression for this piece of code.
  629. ///
  630. /// This is either just _lambda or _lambda re-written to be a generator expression.
  631. /// </summary>
  632. private LambdaExpression GetGeneratorOrNormalLambda() {
  633. LambdaExpression finalCode;
  634. if ((Flags & FunctionAttributes.Generator) == 0) {
  635. finalCode = Code;
  636. } else {
  637. finalCode = Code.ToGenerator(
  638. _lambda.ShouldInterpret,
  639. _lambda.EmitDebugSymbols,
  640. _lambda.GlobalParent.PyContext.Options.CompilationThreshold
  641. );
  642. }
  643. return finalCode;
  644. }
  645. private Delegate CompileLambda(LambdaExpression code, EventHandler<LightLambdaCompileEventArgs> handler) {
  646. if (_lambda.EmitDebugSymbols) {
  647. return CompilerHelpers.CompileToMethod(code, DebugInfoGenerator.CreatePdbGenerator(), true);
  648. } else if (_lambda.ShouldInterpret) {
  649. Delegate result = CompilerHelpers.LightCompile(code, _lambda.GlobalParent.PyContext.Options.CompilationThreshold);
  650. // If the adaptive compiler decides to compile this function, we
  651. // want to store the new compiled target. This saves us from going
  652. // through the interpreter stub every call.
  653. var lightLambda = result.Target as LightLambda;
  654. if (lightLambda != null) {
  655. lightLambda.Compile += handler;
  656. }
  657. return result;
  658. }
  659. return code.Compile();
  660. }
  661. internal Delegate AddRecursionCheck(PythonContext context, Delegate finalTarget) {
  662. if (context.RecursionLimit != Int32.MaxValue) {
  663. if (finalTarget is Func<CodeContext, CodeContext>) {
  664. // no recursion enforcement on classes
  665. return finalTarget;
  666. }
  667. switch (_lambda.ParameterNames.Length) {
  668. #region Generated Python Recursion Delegate Switch
  669. // *** BEGIN GENERATED CODE ***
  670. // generated by function: gen_recursion_delegate_switch from: generate_calls.py
  671. case 0:
  672. finalTarget = new Func<PythonFunction, object>(new PythonFunctionRecursionCheck0((Func<PythonFunction, object>)finalTarget).CallTarget);
  673. break;
  674. case 1:
  675. finalTarget = new Func<PythonFunction, object, object>(new PythonFunctionRecursionCheck1((Func<PythonFunction, object, object>)finalTarget).CallTarget);
  676. break;
  677. case 2:
  678. finalTarget = new Func<PythonFunction, object, object, object>(new PythonFunctionRecursionCheck2((Func<PythonFunction, object, object, object>)finalTarget).CallTarget);
  679. break;
  680. case 3:
  681. finalTarget = new Func<PythonFunction, object, object, object, object>(new PythonFunctionRecursionCheck3((Func<PythonFunction, object, object, object, object>)finalTarget).CallTarget);
  682. break;
  683. case 4:
  684. finalTarget = new Func<PythonFunction, object, object, object, object, object>(new PythonFunctionRecursionCheck4((Func<PythonFunction, object, object, object, object, object>)finalTarget).CallTarget);
  685. break;
  686. case 5:
  687. finalTarget = new Func<PythonFunction, object, object, object, object, object, object>(new PythonFunctionRecursionCheck5((Func<PythonFunction, object, object, object, object, object, object>)finalTarget).CallTarget);
  688. break;
  689. case 6:
  690. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck6((Func<PythonFunction, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  691. break;
  692. case 7:
  693. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck7((Func<PythonFunction, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  694. break;
  695. case 8:
  696. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck8((Func<PythonFunction, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  697. break;
  698. case 9:
  699. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck9((Func<PythonFunction, object, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  700. break;
  701. case 10:
  702. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck10((Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  703. break;
  704. case 11:
  705. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck11((Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  706. break;
  707. case 12:
  708. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck12((Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  709. break;
  710. case 13:
  711. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck13((Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  712. break;
  713. case 14:
  714. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck14((Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  715. break;
  716. case 15:
  717. finalTarget = new Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>(new PythonFunctionRecursionCheck15((Func<PythonFunction, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>)finalTarget).CallTarget);
  718. break;
  719. // *** END GENERATED CODE ***
  720. #endregion
  721. default:
  722. finalTarget = new Func<PythonFunction, object[], object>(new PythonFunctionRecursionCheckN((Func<PythonFunction, object[], object>)finalTarget).CallTarget);
  723. break;
  724. }
  725. }
  726. return finalTarget;
  727. }
  728. class TargetUpdaterForCompilation {
  729. private readonly PythonContext _context;
  730. private readonly FunctionCode _code;
  731. public TargetUpdaterForCompilation(PythonContext context, FunctionCode code) {
  732. _code = code;
  733. _context = context;
  734. }
  735. public void SetCompiledTarget(object sender, Microsoft.Scripting.Interpreter.LightLambdaCompileEventArgs e) {
  736. _code.Target = _code.AddRecursionCheck(_context, _code._normalDelegate = e.Compiled);
  737. }
  738. public void SetCompiledTargetTracing(object sender, Microsoft.Scripting.Interpreter.LightLambdaCompileEventArgs e) {
  739. _code.Target = _code.AddRecursionCheck(_context, _code._tracingDelegate = e.Compiled);
  740. }
  741. }
  742. #region IExpressionSerializable Members
  743. Expression IExpressionSerializable.CreateExpression() {
  744. return Expression.Call(
  745. typeof(PythonOps).GetMethod("MakeFunctionCode"),
  746. Compiler.Ast.PythonAst._globalContext,
  747. Expression.Constant(_lambda.Name),
  748. Expression.Constant(_initialDoc, typeof(string)),
  749. Expression.NewArrayInit(
  750. typeof(string),
  751. ArrayUtils.ConvertAll(_lambda.ParameterNames, (x) => Expression.Constant(x))
  752. ),
  753. Expression.Constant(Flags),
  754. Expression.New(
  755. typeof(SourceSpan).GetConstructor(new Type[] { typeof(SourceLocation), typeof(SourceLocation) }),
  756. Expression.New(
  757. typeof(SourceLocation).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) }),
  758. Expression.Constant(Span.Start.Index),
  759. Expression.Constant(Span.Start.Line),
  760. Expression.Constant(Span.Start.Column)
  761. ),
  762. Expression.New(
  763. typeof(SourceLocation).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) }),
  764. Expression.Constant(Span.End.Index),
  765. Expression.Constant(Span.End.Line),
  766. Expression.Constant(Span.End.Column)
  767. )
  768. ),
  769. Expression.Constant(_lambda.Filename),
  770. GetGeneratorOrNormalLambda(),
  771. TupleToStringArray(co_freevars),
  772. TupleToStringArray(co_names),
  773. TupleToStringArray(co_cellvars),
  774. TupleToStringArray(co_varnames),
  775. Expression.Constant(_localCount)
  776. );
  777. }
  778. private static Expression TupleToStringArray(PythonTuple tuple) {
  779. return tuple.Count > 0 ?
  780. (Expression)Expression.NewArrayInit(
  781. typeof(string),
  782. ArrayUtils.ConvertAll(tuple._data, (x) => Expression.Constant(x))
  783. ) :
  784. (Expression)Expression.Constant(null, typeof(string[]));
  785. }
  786. #endregion
  787. /// <summary>
  788. /// Extremely light weight linked list of weak references used for tracking
  789. /// all of the FunctionCode objects which get created and need to be updated
  790. /// for purposes of recursion enforcement or tracing.
  791. /// </summary>
  792. internal class CodeList {
  793. public readonly WeakReference Code;
  794. public CodeList Next;
  795. public CodeList() { }
  796. public CodeList(WeakReference code, CodeList next) {
  797. Code = code;
  798. Next = next;
  799. }
  800. }
  801. }
  802. internal class PythonDebuggingPayload {
  803. public FunctionCode Code;
  804. private Dictionary<int, bool> _handlerLocations;
  805. private Dictionary<int, Dictionary<int, bool>> _loopAndFinallyLocations;
  806. public PythonDebuggingPayload(FunctionCode code) {
  807. Code = code;
  808. }
  809. public Dictionary<int, bool> HandlerLocations {
  810. get {
  811. if (_handlerLocations == null) {
  812. GatherLocations();
  813. }
  814. return _handlerLocations;
  815. }
  816. }
  817. public Dictionary<int, Dictionary<int, bool>> LoopAndFinallyLocations {
  818. get {
  819. if (_loopAndFinallyLocations == null) {
  820. GatherLocations();
  821. }
  822. return _loopAndFinallyLocations;
  823. }
  824. }
  825. private void GatherLocations() {
  826. var walker = new TracingWalker();
  827. Code.PythonCode.Walk(walker);
  828. _loopAndFinallyLocations = walker.LoopAndFinallyLocations;
  829. _handlerLocations = walker.HandlerLocations;
  830. }
  831. class TracingWalker : Compiler.Ast.PythonWalker {
  832. private bool _inLoop, _inFinally;
  833. private int _loopId;
  834. public Dictionary<int, bool> HandlerLocations = new Dictionary<int, bool>();
  835. public Dictionary<int, Dictionary<int, bool>> LoopAndFinallyLocations = new Dictionary<int, Dictionary<int, bool>>();
  836. private Dictionary<int, bool> _loopIds = new Dictionary<int, bool>();
  837. public override bool Walk(Compiler.Ast.ForStatement node) {
  838. UpdateLoops(node);
  839. WalkLoopBody(node.Body, false);
  840. if (node.Else != null) {
  841. node.Else.Walk(this);
  842. }
  843. return false;
  844. }
  845. private void WalkLoopBody(IronPython.Compiler.Ast.Statement body, bool isFinally) {
  846. bool inLoop = _inLoop;
  847. bool inFinally = _inFinally;
  848. int loopId = ++_loopId;
  849. _inFinally = false;
  850. _inLoop = true;
  851. _loopIds.Add(loopId, isFinally);
  852. body.Walk(this);
  853. _inLoop = inLoop;
  854. _inFinally = inFinally;
  855. LoopOrFinallyIds.Remove(loopId);
  856. }
  857. public override bool Walk(Compiler.Ast.WhileStatement node) {
  858. UpdateLoops(node);
  859. WalkLoopBody(node.Body, false);
  860. if (node.ElseStatement != null) {
  861. node.ElseStatement.Walk(this);
  862. }
  863. return false;
  864. }
  865. public override bool Walk(Compiler.Ast.TryStatement node) {
  866. UpdateLoops(node);
  867. node.Body.Walk(this);
  868. if (node.Handlers != null) {
  869. foreach (var handler in node.Handlers) {
  870. HandlerLocations[handler.Span.Start.Line] = false;
  871. handler.Body.Walk(this);
  872. }
  873. }
  874. if (node.Finally != null) {
  875. WalkLoopBody(node.Finally, true);
  876. }
  877. return false;
  878. }
  879. public override bool Walk(IronPython.Compiler.Ast.AssertStatement node) {
  880. UpdateLoops(node);
  881. return base.Walk(node);
  882. }
  883. public override bool Walk(IronPython.Compiler.Ast.AssignmentStatement node) {
  884. UpdateLoops(node);
  885. return base.Walk(node);
  886. }
  887. public override bool Walk(IronPython.Compiler.Ast.AugmentedAssignStatement node) {
  888. UpdateLoops(node);
  889. return base.Walk(node);
  890. }
  891. public override bool Walk(IronPython.Compiler.Ast.BreakStatement node) {
  892. UpdateLoops(node);
  893. return base.Walk(node);
  894. }
  895. public override bool Walk(IronPython.Compiler.Ast.ClassDefinition node) {
  896. UpdateLoops(node);
  897. return false;
  898. }
  899. public override bool Walk(IronPython.Compiler.Ast.ContinueStatement node) {
  900. UpdateLoops(node);
  901. return base.Walk(node);
  902. }
  903. public override bool Walk(IronPython.Compiler.Ast.ExecStatement node) {
  904. UpdateLoops(node);
  905. return base.Walk(node);
  906. }
  907. public override void PostWalk(IronPython.Compiler.Ast.EmptyStatement node) {
  908. UpdateLoops(node);
  909. base.PostWalk(node);
  910. }
  911. public override bool Walk(IronPython.Compiler.Ast.DelStatement node) {
  912. UpdateLoops(node);
  913. return base.Walk(node);
  914. }
  915. public override bool Walk(IronPython.Compiler.Ast.EmptyStatement node) {
  916. UpdateLoops(node);
  917. return base.Walk(node);
  918. }
  919. public override bool Walk(IronPython.Compiler.Ast.GlobalStatement node) {
  920. UpdateLoops(node);
  921. return base.Walk(node);
  922. }
  923. public override bool Walk(IronPython.Compiler.Ast.FromImportStatement node) {
  924. UpdateLoops(node);
  925. return base.Walk(node);
  926. }
  927. public override bool Walk(IronPython.Compiler.Ast.ExpressionStatement node) {
  928. UpdateLoops(node);
  929. return base.Walk(node);
  930. }
  931. public override bool Walk(IronPython.Compiler.Ast.FunctionDefinition node) {
  932. UpdateLoops(node);
  933. return base.Walk(node);
  934. }
  935. public override bool Walk(IronPython.Compiler.Ast.IfStatement node) {
  936. UpdateLoops(node);
  937. return base.Walk(node);
  938. }
  939. public override bool Walk(IronPython.Compiler.Ast.ImportStatement node) {
  940. UpdateLoops(node);
  941. return base.Walk(node);
  942. }
  943. public override bool Walk(IronPython.Compiler.Ast.RaiseStatement node) {
  944. UpdateLoops(node);
  945. return base.Walk(node);
  946. }
  947. public override bool Walk(IronPython.Compiler.Ast.WithStatement node) {
  948. UpdateLoops(node);
  949. r

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