Skip to content
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

Enumerator based coroutine yielding #281

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/MoonSharp.Interpreter/CoreLib/CoroutineModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,18 @@ public static DynValue status(ScriptExecutionContext executionContext, CallbackA
}

}


[MoonSharpModuleMethod]
public static DynValue is_return_value(ScriptExecutionContext executionContext, CallbackArguments args)
{
return args[0].Type == DataType.ClrCoroutineReturn ? DynValue.True : DynValue.False;
}

[MoonSharpModuleMethod]
public static DynValue get_return_value(ScriptExecutionContext executionContext, CallbackArguments args)
{
return args[0].Type == DataType.ClrCoroutineReturn ? args[0].ClrCoroutineReturnValue : DynValue.Nil;
}

}
}
8 changes: 8 additions & 0 deletions src/MoonSharp.Interpreter/DataTypes/DataType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public enum DataType
/// A request to coroutine.yield
/// </summary>
YieldRequest,
/// <summary>
/// Return a value from a clr coroutine
/// </summary>
ClrCoroutineReturn,
}

/// <summary>
Expand Down Expand Up @@ -114,6 +118,8 @@ public static string ToErrorTypeString(this DataType type)
return "userdata";
case DataType.Thread:
return "coroutine";
case DataType.ClrCoroutineReturn:
return "coroutine_return";
case DataType.Tuple:
case DataType.TailCallRequest:
case DataType.YieldRequest:
Expand Down Expand Up @@ -164,6 +170,8 @@ public static string ToLuaTypeString(this DataType type)
return "userdata";
case DataType.Thread:
return "thread";
case DataType.ClrCoroutineReturn:
return "coroutine_return";
case DataType.Tuple:
case DataType.TailCallRequest:
case DataType.YieldRequest:
Expand Down
13 changes: 13 additions & 0 deletions src/MoonSharp.Interpreter/DataTypes/DynValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public sealed class DynValue
/// Gets the tail call data.
/// </summary>
public UserData UserData { get { return m_Object as UserData; } }

/// <summary>
/// Gets the clr couroutine return value
/// </summary>
public DynValue ClrCoroutineReturnValue { get { return m_Object as DynValue; } }

/// <summary>
/// Returns true if this instance is write protected.
Expand Down Expand Up @@ -373,6 +378,14 @@ public static DynValue NewUserData(UserData userData)
};
}

public static DynValue NewClrCoroutineReturn(DynValue returnedValue) {
return new DynValue()
{
m_Object = returnedValue,
m_Type = DataType.ClrCoroutineReturn,
};
}

/// <summary>
/// Returns this value as readonly - eventually cloning it in the process if it isn't readonly to start with.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace MoonSharp.Interpreter
{
/// <summary>
/// Marks a method as a clr enumerator based coroutine
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public sealed class MoonSharpClrCoroutineAttribute : Attribute
{
public MoonSharpClrCoroutineAttribute()
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private void AddMemberTo(Dictionary<string, IMemberDescriptor> members, string n
{
IOverloadableMemberDescriptor odesc = desc as IOverloadableMemberDescriptor;

if (odesc != null)
if (odesc != null && !(desc is MethodMemberDescriptorCoroutine))
{
if (members.ContainsKey(name))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Reflection;

namespace MoonSharp.Interpreter.Interop
{
public class MethodMemberDescriptorCoroutine : MethodMemberDescriptor
{
public MethodMemberDescriptorCoroutine(MethodBase methodBase, InteropAccessMode accessMode = InteropAccessMode.Default) : base(methodBase, accessMode)
{
}

public override DynValue GetValue(Script script, object obj)
{
var enumerateYielder = script.DoString(@"return function (callable)
return function (...)
for y in callable(...) do
if coroutine.is_return_value(y) then
return coroutine.get_return_value(y)
else
coroutine.yield(y)
end
end
end
end", null, MethodInfo + "_yielder");
Comment on lines +13 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to parse this lua code one for performance reasons? Could this lua function be pulled out and kept as a global function to be used when needed? I've done something similar in my own code, though all on the lua side without the C# interconnection work this PR does to make it nice and clean.

return script.Call(enumerateYielder, base.GetValue(script, obj));
}

/// Tries to create a new MethodMemberDescriptorCoroutine, returning
/// <c>null</c> in case the method is not
/// visible to script code.
/// </summary>
/// <param name="methodBase">The MethodBase.</param>
/// <param name="accessMode">The <see cref="InteropAccessMode" /></param>
/// <param name="forceVisibility">if set to <c>true</c> forces visibility.</param>
/// <returns>
/// A new MethodMemberDescriptor or null.
/// </returns>
public static MethodMemberDescriptorCoroutine TryCreateCoroutineIfVisible(MethodBase methodBase, InteropAccessMode accessMode, bool forceVisibility = false)
{
if (!CheckMethodIsCompatible(methodBase, false))
return null;

if (forceVisibility || (methodBase.GetVisibilityFromAttributes() ?? methodBase.IsPublic))
return new MethodMemberDescriptorCoroutine(methodBase, accessMode);

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ private void FillMemberList()
{
if (membersToIgnore.Contains(mi.Name)) continue;

MethodMemberDescriptor md = MethodMemberDescriptor.TryCreateIfVisible(mi, this.AccessMode);
MethodMemberDescriptor md;
if (mi.CustomAttributes.Any(data => data.AttributeType == typeof(MoonSharpClrCoroutineAttribute))) {
md = MethodMemberDescriptorCoroutine.TryCreateCoroutineIfVisible(mi, this.AccessMode);
} else {
md = MethodMemberDescriptor.TryCreateIfVisible(mi, this.AccessMode);
}

if (md != null)
{
Expand Down