From b8ea3c5507f98278432e16e9803f5036e2131eb5 Mon Sep 17 00:00:00 2001 From: Ahmed Yasin Koculu Date: Wed, 20 Dec 2023 02:46:47 +0100 Subject: [PATCH] Await tasks, runtime tasks and valuetasks with correct return type. --- src/Topaz.Test/AwaitTests.cs | 101 ++++++++++++++++++ .../Expressions/AwaitExpressionHandler.cs | 20 +++- .../Expressions/AwaitExpressionHandler.cs | 22 +++- src/Topaz/Directory.Build.props | 4 +- 4 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 src/Topaz.Test/AwaitTests.cs diff --git a/src/Topaz.Test/AwaitTests.cs b/src/Topaz.Test/AwaitTests.cs new file mode 100644 index 0000000..78e1f22 --- /dev/null +++ b/src/Topaz.Test/AwaitTests.cs @@ -0,0 +1,101 @@ +using NUnit.Framework; +using System.Collections; +using System.Threading.Tasks; +using Tenray.Topaz.API; + +namespace Tenray.Topaz.Test; + +public sealed class AwaitTests +{ + public async ValueTask GenericValueTask() + { + await Task.Delay(1); + return 33; + } + + public async ValueTask SimpleValueTask() + { + await Task.Delay(1); + return; + } + + public async Task GenericTask() + { + await Task.Delay(1); + return 33; + } + + public async Task SimpleTask() + { + await Task.Delay(1); + return; + } + + [Test] + public void GenericValueTaskAwait() + { + var engine = new TopazEngine(); + dynamic model = new JsObject(); + engine.SetValue("test", this); + engine.SetValue("model", model); + engine.ExecuteScriptAsync(@" +model.result1 = await test.GenericValueTask(); +").Wait(); + Assert.That(model.result1, Is.EqualTo(33)); + engine.ExecuteScript(@" +model.result2 = await test.GenericValueTask(); +"); + Assert.That(model.result2, Is.EqualTo(33)); + } + + [Test] + public void SimpleValueTaskAwait() + { + var engine = new TopazEngine(); + dynamic model = new JsObject(); + engine.SetValue("test", this); + engine.SetValue("model", model); + engine.ExecuteScriptAsync(@" +model.result1 = await test.SimpleValueTask(); +").Wait(); + Assert.That(model.result1, Is.EqualTo(null)); + engine.ExecuteScript(@" +model.result2 = await test.SimpleValueTask(); +"); + Assert.That(model.result2, Is.EqualTo(null)); + } + + [Test] + public void GenericTaskAwait() + { + var engine = new TopazEngine(); + dynamic model = new JsObject(); + engine.SetValue("test", this); + engine.SetValue("model", model); + engine.ExecuteScriptAsync(@" +model.result1 = await test.GenericTask(); +").Wait(); + Assert.That(model.result1, Is.EqualTo(33)); + engine.ExecuteScript(@" +model.result2 = await test.GenericTask(); +"); + Assert.That(model.result2, Is.EqualTo(33)); + } + + [Test] + public void SimpleTaskAwait() + { + var engine = new TopazEngine(); + dynamic model = new JsObject(); + engine.SetValue("test", this); + engine.SetValue("model", model); + engine.ExecuteScriptAsync(@" +model.result1 = await test.SimpleTask(); +").Wait(); + Assert.That(model.result1, Is.EqualTo(null)); + engine.ExecuteScript(@" +model.result2 = await test.SimpleTask(); +"); + Assert.That(model.result2, Is.EqualTo(null)); + } +} \ No newline at end of file diff --git a/src/Topaz/AstHandlers/AsyncHandlers/Expressions/AwaitExpressionHandler.cs b/src/Topaz/AstHandlers/AsyncHandlers/Expressions/AwaitExpressionHandler.cs index 817103f..af9d14e 100644 --- a/src/Topaz/AstHandlers/AsyncHandlers/Expressions/AwaitExpressionHandler.cs +++ b/src/Topaz/AstHandlers/AsyncHandlers/Expressions/AwaitExpressionHandler.cs @@ -13,12 +13,30 @@ internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecut var result = await scriptExecutor.ExecuteStatementAsync(expr.Argument, token); if (result == null) return null; - if (result is Task || result is ValueTask) + if (result is Task task) + { + var type = task.GetType(); + var returnType = type.GetMethod("GetAwaiter").ReturnType.GetMethod("GetResult").ReturnType; + if (returnType != typeof(void) && returnType.FullName != "System.Threading.Tasks.VoidTaskResult") + return await (dynamic)task; + await task; + return null; + } + + if (result is ValueTask valueTask) + { + await valueTask; + return null; + } + + var type2 = result.GetType(); + if (type2.IsGenericType && typeof(ValueTask<>) == type2.GetGenericTypeDefinition()) { dynamic awaitable = result; if (result.GetType().IsGenericType) return await awaitable; await awaitable; + return null; } return result; } diff --git a/src/Topaz/AstHandlers/Expressions/AwaitExpressionHandler.cs b/src/Topaz/AstHandlers/Expressions/AwaitExpressionHandler.cs index 7e28c69..40404ea 100644 --- a/src/Topaz/AstHandlers/Expressions/AwaitExpressionHandler.cs +++ b/src/Topaz/AstHandlers/Expressions/AwaitExpressionHandler.cs @@ -14,18 +14,32 @@ internal static object Execute(ScriptExecutor scriptExecutor, Node expression, C var result = scriptExecutor.ExecuteStatement(expr.Argument, token); if (result is Task task) { - return GetTaskResult(task); + task.Wait(token); + var type = task.GetType(); + var returnType = type.GetMethod("GetAwaiter").ReturnType.GetMethod("GetResult").ReturnType; + if (returnType != typeof(void) && returnType.FullName != "System.Threading.Tasks.VoidTaskResult") + return GetTaskResult(task); + return null; + } + + if (result is ValueTask valueTask) + { + valueTask.AsTask().Wait(token); + return null; } - else if (result is ValueTask valueTask) + + var type2 = result.GetType(); + if (type2.IsGenericType && typeof(ValueTask<>) == type2.GetGenericTypeDefinition()) { - return GetTaskResult(valueTask.AsTask()); + dynamic r = result; + r.AsTask().Wait(token); + return r.Result; } return result; } internal static object GetTaskResult(Task task) { - task.Wait(); var property = task.GetType().GetProperty("Result", BindingFlags.Public | BindingFlags.Instance); if (property == null) diff --git a/src/Topaz/Directory.Build.props b/src/Topaz/Directory.Build.props index f80d458..885604e 100644 --- a/src/Topaz/Directory.Build.props +++ b/src/Topaz/Directory.Build.props @@ -5,8 +5,8 @@ Ahmed Yasin Koculu Topaz Topaz - 1.3.7.0 - 1.3.7.0 + 1.3.8.0 + 1.3.8.0 Ahmed Yasin Koculu Topaz Multithreaded Javascript Engine for .NET