From 8640231ccc3007431fb98fbdf5c9e8e8ef6b0331 Mon Sep 17 00:00:00 2001 From: liuyan Date: Tue, 22 Jul 2025 11:14:54 +0800 Subject: [PATCH 1/3] feat: Add configurable timeout support for UnwrapIfPromise in async disposal and await operations --- Jint.Tests/Runtime/AsyncTests.cs | 29 +++++++++++++++++-- Jint/Native/Disposable/DisposeCapability.cs | 2 +- Jint/Options.cs | 6 ++++ .../Expressions/JintAwaitExpression.cs | 2 +- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Jint.Tests/Runtime/AsyncTests.cs b/Jint.Tests/Runtime/AsyncTests.cs index da31e21332..512ab7cf23 100644 --- a/Jint.Tests/Runtime/AsyncTests.cs +++ b/Jint.Tests/Runtime/AsyncTests.cs @@ -41,6 +41,29 @@ public void ShouldReturnedTaskConvertedToPromiseInJS() result = result.UnwrapIfPromise(); Assert.Equal(AsyncTestClass.TestString, result); } + [Fact] + public void ShouldUnwrapPromiseWithCustomTimeout() + { + Engine engine = new(options => options.ExperimentalFeatures = ExperimentalFeature.TaskInterop); + engine.SetValue("asyncTestClass", new AsyncTestClass()); + var result = engine.Evaluate("asyncTestClass.ReturnDelayedTaskAsync().then(x=>x)"); + result = result.UnwrapIfPromise(TimeSpan.FromMilliseconds(200)); + Assert.Equal(AsyncTestClass.TestString, result); + } + + [Fact] + public void ShouldAwaitUnwrapPromiseWithCustomTimeout() + { + Engine engine = new(options => { options.ExperimentalFeatures = ExperimentalFeature.TaskInterop; options.Constraints.UnwrapIfPromiseTimeout = TimeSpan.FromMilliseconds(200); }); + engine.SetValue("asyncTestClass", new AsyncTestClass()); + engine.Execute(""" + async function test() { + return await asyncTestClass.ReturnDelayedTaskAsync(); + } + """); + var result = engine.Invoke("test").UnwrapIfPromise(); + Assert.Equal(AsyncTestClass.TestString, result); + } [Fact] public void ShouldReturnedCompletedTaskConvertedToPromiseInJS() @@ -230,7 +253,7 @@ async function foo(name) { Assert.Equal(expected, log.Select(x => x.AsString()).ToArray()); } - + [Fact] public void ShouldPromiseBeResolved() { @@ -240,7 +263,7 @@ public void ShouldPromiseBeResolved() { log.Add(str); }); - + const string Script = """ async function main() { return new Promise(function (resolve) { @@ -268,7 +291,7 @@ public void ShouldPromiseBeResolved2() { Task.Delay(ms).ContinueWith(_ => action()); }); - + const string Script = """ var delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); async function main() { diff --git a/Jint/Native/Disposable/DisposeCapability.cs b/Jint/Native/Disposable/DisposeCapability.cs index e358e70daf..1157fa4214 100644 --- a/Jint/Native/Disposable/DisposeCapability.cs +++ b/Jint/Native/Disposable/DisposeCapability.cs @@ -87,7 +87,7 @@ public Completion DisposeResources(Completion c) hasAwaited = true; try { - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(_engine.Options.Constraints.UnwrapIfPromiseTimeout); } catch (JavaScriptException e) { diff --git a/Jint/Options.cs b/Jint/Options.cs index 5e30581841..e44fcf1b8a 100644 --- a/Jint/Options.cs +++ b/Jint/Options.cs @@ -431,6 +431,12 @@ public class ConstraintOptions /// public TimeSpan RegexTimeout { get; set; } = TimeSpan.FromSeconds(10); + + /// + /// Maximum time allowed for unwrapping a Promise and getting its resolved/rejected value. + /// Defaults to 10 seconds. + /// + public TimeSpan UnwrapIfPromiseTimeout { get; set; } = TimeSpan.FromSeconds(10); /// /// The maximum size for JavaScript array, defaults to . /// diff --git a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs index 22f5c047c8..082fe611b7 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs @@ -34,7 +34,7 @@ protected override object EvaluateInternal(EvaluationContext context) value = promiseInstance; } - return value.UnwrapIfPromise(); + return value.UnwrapIfPromise(engine.Options.Constraints.UnwrapIfPromiseTimeout); } catch (PromiseRejectedException e) { From 37bdf94cefee5630ef12ba91a9fae740a0a51636 Mon Sep 17 00:00:00 2001 From: liuyan Date: Tue, 22 Jul 2025 13:09:25 +0800 Subject: [PATCH 2/3] fix: mac unit test 200ms to 500ms --- Jint.Tests/Runtime/AsyncTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jint.Tests/Runtime/AsyncTests.cs b/Jint.Tests/Runtime/AsyncTests.cs index 512ab7cf23..bdc250d06b 100644 --- a/Jint.Tests/Runtime/AsyncTests.cs +++ b/Jint.Tests/Runtime/AsyncTests.cs @@ -54,7 +54,7 @@ public void ShouldUnwrapPromiseWithCustomTimeout() [Fact] public void ShouldAwaitUnwrapPromiseWithCustomTimeout() { - Engine engine = new(options => { options.ExperimentalFeatures = ExperimentalFeature.TaskInterop; options.Constraints.UnwrapIfPromiseTimeout = TimeSpan.FromMilliseconds(200); }); + Engine engine = new(options => { options.ExperimentalFeatures = ExperimentalFeature.TaskInterop; options.Constraints.UnwrapIfPromiseTimeout = TimeSpan.FromMilliseconds(500); }); engine.SetValue("asyncTestClass", new AsyncTestClass()); engine.Execute(""" async function test() { From bd564e82c2aa3a5eb4b4225d5b066f822bbbcff3 Mon Sep 17 00:00:00 2001 From: liuyan Date: Wed, 23 Jul 2025 16:34:15 +0800 Subject: [PATCH 3/3] change to PromiseTimeout and format lines --- Jint.Tests/Runtime/AsyncTests.cs | 3 ++- Jint/Native/Disposable/DisposeCapability.cs | 2 +- Jint/Options.cs | 4 ++-- Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Jint.Tests/Runtime/AsyncTests.cs b/Jint.Tests/Runtime/AsyncTests.cs index bdc250d06b..fe350fb7a6 100644 --- a/Jint.Tests/Runtime/AsyncTests.cs +++ b/Jint.Tests/Runtime/AsyncTests.cs @@ -41,6 +41,7 @@ public void ShouldReturnedTaskConvertedToPromiseInJS() result = result.UnwrapIfPromise(); Assert.Equal(AsyncTestClass.TestString, result); } + [Fact] public void ShouldUnwrapPromiseWithCustomTimeout() { @@ -54,7 +55,7 @@ public void ShouldUnwrapPromiseWithCustomTimeout() [Fact] public void ShouldAwaitUnwrapPromiseWithCustomTimeout() { - Engine engine = new(options => { options.ExperimentalFeatures = ExperimentalFeature.TaskInterop; options.Constraints.UnwrapIfPromiseTimeout = TimeSpan.FromMilliseconds(500); }); + Engine engine = new(options => { options.ExperimentalFeatures = ExperimentalFeature.TaskInterop; options.Constraints.PromiseTimeout = TimeSpan.FromMilliseconds(500); }); engine.SetValue("asyncTestClass", new AsyncTestClass()); engine.Execute(""" async function test() { diff --git a/Jint/Native/Disposable/DisposeCapability.cs b/Jint/Native/Disposable/DisposeCapability.cs index 1157fa4214..66984b1c0f 100644 --- a/Jint/Native/Disposable/DisposeCapability.cs +++ b/Jint/Native/Disposable/DisposeCapability.cs @@ -87,7 +87,7 @@ public Completion DisposeResources(Completion c) hasAwaited = true; try { - result = result.UnwrapIfPromise(_engine.Options.Constraints.UnwrapIfPromiseTimeout); + result = result.UnwrapIfPromise(_engine.Options.Constraints.PromiseTimeout); } catch (JavaScriptException e) { diff --git a/Jint/Options.cs b/Jint/Options.cs index e44fcf1b8a..2d69eb57eb 100644 --- a/Jint/Options.cs +++ b/Jint/Options.cs @@ -431,12 +431,12 @@ public class ConstraintOptions /// public TimeSpan RegexTimeout { get; set; } = TimeSpan.FromSeconds(10); - /// /// Maximum time allowed for unwrapping a Promise and getting its resolved/rejected value. /// Defaults to 10 seconds. /// - public TimeSpan UnwrapIfPromiseTimeout { get; set; } = TimeSpan.FromSeconds(10); + public TimeSpan PromiseTimeout { get; set; } = TimeSpan.FromSeconds(10); + /// /// The maximum size for JavaScript array, defaults to . /// diff --git a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs index 082fe611b7..1562873e01 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs @@ -34,7 +34,7 @@ protected override object EvaluateInternal(EvaluationContext context) value = promiseInstance; } - return value.UnwrapIfPromise(engine.Options.Constraints.UnwrapIfPromiseTimeout); + return value.UnwrapIfPromise(engine.Options.Constraints.PromiseTimeout); } catch (PromiseRejectedException e) {