-
-
Notifications
You must be signed in to change notification settings - Fork 547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experimental Stackguard #1566
Experimental Stackguard #1566
Changes from all commits
ae4e475
20e4090
fc20b80
e12738a
49e33b9
d816c75
a2cbeb9
b7cc80c
585d8e6
9371156
558d8ba
91e5d1b
2f19ebe
30a7b91
4f6d7e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
using Jint.Runtime.Debugger; | ||
using Jint.Runtime.Descriptors; | ||
using Jint.Runtime.Modules; | ||
using Jint.Runtime.CallStack; | ||
|
||
namespace Jint | ||
{ | ||
|
@@ -385,6 +386,16 @@ public class ConstraintOptions | |
/// </summary> | ||
public int MaxRecursionDepth { get; set; } = -1; | ||
|
||
/// <summary> | ||
/// Maximum recursion stack count, defaults to -1 (as-is dotnet stacktrace). | ||
/// </summary> | ||
/// <remarks> | ||
/// Chrome and V8 based engines (ClearScript) that can handle 13955. | ||
/// When set to a different value except -1, it can reduce slight performance/stack trace readability drawback. (after hitting the engine's own limit), | ||
/// When max stack size to be exceeded, Engine throws an exception <see cref="JavaScriptException">. | ||
/// </remarks> | ||
public int MaxExecutionStackCount { get; set; } = StackGuard.Disabled; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the old comment had valuable info There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I updated it as |
||
|
||
/// <summary> | ||
/// Maximum time a Regex is allowed to run, defaults to 10 seconds. | ||
/// </summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
// https://github.com/dotnet/runtime/blob/a0964f9e3793cb36cc01d66c14a61e89ada5e7da/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/StackGuard.cs | ||
|
||
using System.Runtime.CompilerServices; | ||
using System.Threading; | ||
|
||
namespace Jint.Runtime.CallStack; | ||
|
||
internal sealed class StackGuard | ||
{ | ||
public const int Disabled = -1; | ||
|
||
private readonly Engine _engine; | ||
|
||
public StackGuard(Engine engine) | ||
{ | ||
_engine = engine; | ||
} | ||
|
||
public bool TryEnterOnCurrentStack() | ||
{ | ||
if (_engine.Options.Constraints.MaxExecutionStackCount == Disabled) | ||
{ | ||
return true; | ||
} | ||
|
||
#if NETFRAMEWORK || NETSTANDARD2_0 | ||
try | ||
{ | ||
RuntimeHelpers.EnsureSufficientExecutionStack(); | ||
return true; | ||
} | ||
catch (InsufficientExecutionStackException) | ||
{ | ||
} | ||
#else | ||
if (RuntimeHelpers.TryEnsureSufficientExecutionStack()) | ||
{ | ||
return true; | ||
} | ||
#endif | ||
|
||
if (_engine.CallStack.Count > _engine.Options.Constraints.MaxExecutionStackCount) | ||
{ | ||
ExceptionHelper.ThrowRangeError(_engine.Realm, "Maximum call stack size exceeded"); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public TR RunOnEmptyStack<T1, TR>(Func<T1, TR> action, T1 arg1) | ||
{ | ||
#if NETFRAMEWORK || NETSTANDARD2_0 | ||
return RunOnEmptyStackCore(static s => | ||
{ | ||
var t = (Tuple<Func<T1, TR>, T1>) s; | ||
return t.Item1(t.Item2); | ||
}, Tuple.Create(action, arg1)); | ||
#else | ||
// Prefer ValueTuple when available to reduce dependencies on Tuple | ||
return RunOnEmptyStackCore(static s => | ||
{ | ||
var t = ((Func<T1, TR>, T1)) s; | ||
return t.Item1(t.Item2); | ||
}, (action, arg1)); | ||
#endif | ||
|
||
} | ||
|
||
private R RunOnEmptyStackCore<R>(Func<object, R> action, object state) | ||
{ | ||
// Using default scheduler rather than picking up the current scheduler. | ||
Task<R> task = Task.Factory.StartNew((Func<object?, R>) action, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); | ||
|
||
// Avoid AsyncWaitHandle lazy allocation of ManualResetEvent in the rare case we finish quickly. | ||
if (!task.IsCompleted) | ||
{ | ||
// Task.Wait has the potential of inlining the task's execution on the current thread; avoid this. | ||
((IAsyncResult) task).AsyncWaitHandle.WaitOne(); | ||
} | ||
|
||
// Using awaiter here to propagate original exception | ||
return task.GetAwaiter().GetResult(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe no console write lines, they just make test output bad - maybe just return needed data from
Evaluate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log
removed.I used it for testing locally and did not expect to effect test output :)