Skip to content

Commit

Permalink
Await tasks, runtime tasks and valuetasks with correct return type.
Browse files Browse the repository at this point in the history
  • Loading branch information
koculu committed Dec 20, 2023
1 parent 1acd365 commit b8ea3c5
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 7 deletions.
101 changes: 101 additions & 0 deletions src/Topaz.Test/AwaitTests.cs
Original file line number Diff line number Diff line change
@@ -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<int> GenericValueTask()
{
await Task.Delay(1);
return 33;
}

public async ValueTask SimpleValueTask()
{
await Task.Delay(1);
return;
}

public async Task<int> 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,30 @@ internal async static ValueTask<object> 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;
}
Expand Down
22 changes: 18 additions & 4 deletions src/Topaz/AstHandlers/Expressions/AwaitExpressionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/Topaz/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<Authors>Ahmed Yasin Koculu</Authors>
<PackageId>Topaz</PackageId>
<Title>Topaz</Title>
<ProductVersion>1.3.7.0</ProductVersion>
<Version>1.3.7.0</Version>
<ProductVersion>1.3.8.0</ProductVersion>
<Version>1.3.8.0</Version>
<Authors>Ahmed Yasin Koculu</Authors>
<AssemblyTitle>Topaz</AssemblyTitle>
<Description>Multithreaded Javascript Engine for .NET</Description>
Expand Down

0 comments on commit b8ea3c5

Please sign in to comment.