StackGuard.cs 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // https://github.com/dotnet/runtime/blob/a0964f9e3793cb36cc01d66c14a61e89ada5e7da/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/StackGuard.cs
  4. using System.Runtime.CompilerServices;
  5. using System.Threading;
  6. namespace Jint.Runtime.CallStack;
  7. internal sealed class StackGuard
  8. {
  9. public const int Disabled = -1;
  10. private readonly Engine _engine;
  11. public StackGuard(Engine engine)
  12. {
  13. _engine = engine;
  14. }
  15. public bool TryEnterOnCurrentStack()
  16. {
  17. if (_engine.Options.Constraints.MaxExecutionStackCount == Disabled)
  18. {
  19. return true;
  20. }
  21. #if NETFRAMEWORK || NETSTANDARD2_0
  22. try
  23. {
  24. RuntimeHelpers.EnsureSufficientExecutionStack();
  25. return true;
  26. }
  27. catch (InsufficientExecutionStackException)
  28. {
  29. }
  30. #else
  31. if (RuntimeHelpers.TryEnsureSufficientExecutionStack())
  32. {
  33. return true;
  34. }
  35. #endif
  36. if (_engine.CallStack.Count > _engine.Options.Constraints.MaxExecutionStackCount)
  37. {
  38. Throw.RangeError(_engine.Realm, "Maximum call stack size exceeded");
  39. }
  40. return false;
  41. }
  42. public static TR RunOnEmptyStack<T1, TR>(Func<T1, TR> action, T1 arg1)
  43. {
  44. #if NETFRAMEWORK || NETSTANDARD2_0
  45. return RunOnEmptyStackCore(static s =>
  46. {
  47. var t = (Tuple<Func<T1, TR>, T1>) s;
  48. return t.Item1(t.Item2);
  49. }, Tuple.Create(action, arg1));
  50. #else
  51. // Prefer ValueTuple when available to reduce dependencies on Tuple
  52. return RunOnEmptyStackCore(static s =>
  53. {
  54. var t = ((Func<T1, TR>, T1)) s;
  55. return t.Item1(t.Item2);
  56. }, (action, arg1));
  57. #endif
  58. }
  59. private static R RunOnEmptyStackCore<R>(Func<object, R> action, object state)
  60. {
  61. // Using default scheduler rather than picking up the current scheduler.
  62. Task<R> task = Task.Factory.StartNew((Func<object?, R>) action, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  63. // Avoid AsyncWaitHandle lazy allocation of ManualResetEvent in the rare case we finish quickly.
  64. if (!task.IsCompleted)
  65. {
  66. // Task.Wait has the potential of inlining the task's execution on the current thread; avoid this.
  67. ((IAsyncResult) task).AsyncWaitHandle.WaitOne();
  68. }
  69. // Using awaiter here to propagate original exception
  70. return task.GetAwaiter().GetResult();
  71. }
  72. }