Skip to content

Commit 0b9b200

Browse files
committed
Remove open delegate marshaling
1 parent 2c0573d commit 0b9b200

12 files changed

Lines changed: 363 additions & 181 deletions

File tree

src/Laylua/Library/Entities/Reference/LuaReference.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,9 @@ internal static bool TryCreate(lua_State* L, int stackIndex, out int reference)
253253
lua_pushvalue(L, stackIndex);
254254
reference = luaL_ref(L, LuaRegistry.Index);
255255
if (reference is LUA_REFNIL or LUA_NOREF)
256+
{
256257
return false;
258+
}
257259

258260
return true;
259261
}

src/Laylua/Library/Marshaler/DefaultLuaMarshaler.PushValue.Function.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ protected virtual void PushDelegate(LuaThread thread, Delegate @delegate)
129129
lua_setmetatable(L, -2);
130130
isMetatableOnStack = false;
131131

132-
lua_pushcclosure(L, DelegateCFunction, 1);
132+
lua_pushlightuserdata(L, (void*) LayluaNative.getPotentialPanicPtr);
133+
lua_pushcclosure(L, DelegateCFunction, 2);
133134
isUserdataOnStack = false;
134135
}
135136
catch
Lines changed: 84 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,42 @@
11
using System;
2-
using System.Collections.Concurrent;
32
using System.Reflection;
3+
using System.Runtime.InteropServices;
44
using Laylua.Moon;
55

66
namespace Laylua.Marshaling;
77

88
public unsafe partial class DefaultLuaMarshaler
99
{
10-
private const int MaxSupportedDelegateParameterCount = 16;
11-
private const int MaxSupportedDelegateTypeArgumentCount = MaxSupportedDelegateParameterCount + 1;
12-
13-
private static readonly ConcurrentDictionary<Type, DelegateWrapperMethodCacheEntry> _delegateWrapperMethods = new();
14-
private static readonly MethodInfo?[] _actionWrapperMethodDefinitions = BuildDelegateWrapperMethodDefinitions(nameof(LuaFunctionDelegateWrapper.InvokeAction));
15-
private static readonly MethodInfo?[] _functionWrapperMethodDefinitions = BuildDelegateWrapperMethodDefinitions(nameof(LuaFunctionDelegateWrapper.InvokeFunc));
16-
17-
/// <summary>
18-
/// Wraps a <see cref="LuaFunction"/> so it can be called as a .NET delegate.
19-
/// </summary>
20-
private sealed partial class LuaFunctionDelegateWrapper(LuaFunction function);
21-
22-
private readonly struct DelegateWrapperMethodCacheEntry(MethodInfo? method, int parameterCount, bool hasTooManyParameters)
23-
{
24-
public readonly MethodInfo? Method = method;
25-
26-
public readonly int ParameterCount = parameterCount;
27-
28-
public readonly bool HasTooManyParameters = hasTooManyParameters;
29-
}
30-
31-
internal static Delegate? CreateDelegateFromFunction(LuaFunction function, Type delegateType)
10+
internal static bool CanConvertFunctionToDelegate(lua_State* L, int stackIndex, Type delegateType)
3211
{
33-
var cacheEntry = _delegateWrapperMethods.GetOrAdd(delegateType, static currentDelegateType => CreateDelegateWrapperMethodCacheEntry(currentDelegateType));
34-
if (cacheEntry.HasTooManyParameters)
12+
if (delegateType.BaseType != typeof(MulticastDelegate))
3513
{
36-
throw new NotSupportedException(
37-
$"Cannot marshal a Lua function to delegate type '{delegateType.Name}' because it has {cacheEntry.ParameterCount} parameters. " +
38-
$"Marshaling Lua functions to delegates supports at most {MaxSupportedDelegateParameterCount} parameters. " +
39-
$"Use {nameof(LuaFunction)} directly for higher-arity calls.");
14+
return false;
4015
}
4116

42-
if (cacheEntry.Method == null)
43-
{
44-
return null;
45-
}
17+
return TryGetCallableTargetFromFunction(L, stackIndex, delegateType, out _);
18+
}
4619

47-
var wrapper = new LuaFunctionDelegateWrapper(function);
48-
return cacheEntry.Method.CreateDelegate(delegateType, wrapper);
20+
internal static bool CanConvertFunctionToMethodInfo(lua_State* L, int stackIndex)
21+
{
22+
return TryGetMethodInfoFromFunction(L, stackIndex, typeof(MethodInfo), out _);
4923
}
5024

51-
internal static bool CanConvertFunctionToDelegate(lua_State* L, int stackIndex, Type delegateType)
25+
internal static bool TryGetCallableTargetFromFunction(lua_State* L, int stackIndex, Type targetType, out object? result)
5226
{
53-
if (delegateType.BaseType != typeof(MulticastDelegate))
27+
if (TryGetMethodInfoFromFunction(L, stackIndex, targetType, out result))
5428
{
55-
return false;
29+
return true;
5630
}
5731

58-
if (TryGetMethodInfoFromFunction(L, stackIndex, delegateType, out _))
32+
if (targetType.BaseType == typeof(MulticastDelegate) && TryGetManagedDelegateFromFunction(L, stackIndex, targetType, out var @delegate))
5933
{
34+
result = @delegate;
6035
return true;
6136
}
6237

63-
var cacheEntry = _delegateWrapperMethods.GetOrAdd(delegateType, static currentDelegateType => CreateDelegateWrapperMethodCacheEntry(currentDelegateType));
64-
return !cacheEntry.HasTooManyParameters && cacheEntry.Method != null;
65-
}
66-
67-
internal static bool CanConvertFunctionToMethodInfo(lua_State* L, int stackIndex)
68-
{
69-
return TryGetMethodInfoFromFunction(L, stackIndex, typeof(MethodInfo), out _);
38+
result = null;
39+
return false;
7040
}
7141

7242
/// <summary>
@@ -118,89 +88,100 @@ private static bool TryGetMethodInfoFromFunction(lua_State* L, int stackIndex, T
11888
return false;
11989
}
12090

121-
private static DelegateWrapperMethodCacheEntry CreateDelegateWrapperMethodCacheEntry(Type delegateType)
91+
private static bool TryGetManagedDelegateFromFunction(lua_State* L, int stackIndex, Type delegateType, out Delegate? result)
12292
{
123-
var invokeMethod = delegateType.GetMethod("Invoke");
124-
if (invokeMethod == null)
93+
stackIndex = lua_absindex(L, stackIndex);
94+
var top = lua_gettop(L);
95+
try
12596
{
126-
return default;
127-
}
97+
if (!IsManagedDelegateClosure(L, stackIndex))
98+
{
99+
result = null;
100+
return false;
101+
}
128102

129-
var parameters = invokeMethod.GetParameters();
130-
var parameterCount = parameters.Length;
131-
if (parameterCount > MaxSupportedDelegateParameterCount)
132-
{
133-
return new DelegateWrapperMethodCacheEntry(method: null, parameterCount, hasTooManyParameters: true);
134-
}
103+
if (lua_getupvalue(L, stackIndex, 1).Pointer == null)
104+
{
105+
result = null;
106+
return false;
107+
}
135108

136-
var returnType = invokeMethod.ReturnType;
137-
var isVoid = returnType == typeof(void);
138-
var typeArgumentCount = isVoid
139-
? parameterCount
140-
: parameterCount + 1;
109+
if (lua_type(L, -1) != LuaType.UserData || !IsDelegateHandleUserData(L, -1))
110+
{
111+
result = null;
112+
return false;
113+
}
141114

142-
var typeArguments = new Type[typeArgumentCount];
143-
for (var i = 0; i < parameterCount; i++)
144-
{
145-
var parameterType = parameters[i].ParameterType;
146-
if (IsUnsupportedDelegateType(parameterType))
115+
var ptr = (nint*) lua_touserdata(L, -1);
116+
if (ptr == null || *ptr == 0)
147117
{
148-
return default;
118+
result = null;
119+
return false;
149120
}
150121

151-
typeArguments[i] = parameterType;
152-
}
122+
var handlePtr = *ptr;
123+
if (!IsDelegateHandleTracked(Lua.FromExtraSpace(L), handlePtr))
124+
{
125+
result = null;
126+
return false;
127+
}
153128

154-
if (!isVoid)
155-
{
156-
if (IsUnsupportedDelegateType(returnType))
129+
var handle = GCHandle.FromIntPtr(handlePtr);
130+
if (handle.Target is Delegate @delegate && delegateType.IsInstanceOfType(@delegate))
157131
{
158-
return default;
132+
result = @delegate;
133+
return true;
159134
}
160135

161-
typeArguments[parameterCount] = returnType;
136+
result = null;
137+
return false;
162138
}
139+
finally
140+
{
141+
lua_settop(L, top);
142+
}
143+
}
163144

164-
var methodDefinition = isVoid
165-
? _actionWrapperMethodDefinitions[typeArgumentCount]
166-
: _functionWrapperMethodDefinitions[typeArgumentCount];
145+
private static bool IsManagedDelegateClosure(lua_State* L, int stackIndex)
146+
{
147+
if (!lua_iscfunction(L, stackIndex))
148+
{
149+
return false;
150+
}
167151

168-
if (methodDefinition == null)
152+
if (lua_getupvalue(L, stackIndex, 2).Pointer == null)
169153
{
170-
return default;
154+
return false;
171155
}
172156

173-
var method = typeArgumentCount == 0
174-
? methodDefinition
175-
: methodDefinition.MakeGenericMethod(typeArguments);
157+
if (lua_type(L, -1) != LuaType.LightUserData
158+
|| (nint) lua_touserdata(L, -1) != (nint) LayluaNative.getPotentialPanicPtr)
159+
{
160+
lua_pop(L, 1);
161+
return false;
162+
}
176163

177-
return new DelegateWrapperMethodCacheEntry(method, parameterCount, hasTooManyParameters: false);
164+
lua_pop(L, 1);
165+
return true;
178166
}
179167

180-
private static MethodInfo?[] BuildDelegateWrapperMethodDefinitions(string methodName)
168+
private static bool IsDelegateHandleUserData(lua_State* L, int stackIndex)
181169
{
182-
var methodDefinitions = new MethodInfo?[MaxSupportedDelegateTypeArgumentCount + 1];
183-
var methods = typeof(LuaFunctionDelegateWrapper).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
184-
for (var i = 0; i < methods.Length; i++)
170+
stackIndex = lua_absindex(L, stackIndex);
171+
if (!lua_getmetatable(L, stackIndex))
185172
{
186-
var method = methods[i];
187-
if (method.Name != methodName)
188-
{
189-
continue;
190-
}
173+
return false;
174+
}
191175

192-
var genericArgumentCount = method.GetGenericArguments().Length;
193-
if (genericArgumentCount <= MaxSupportedDelegateTypeArgumentCount)
194-
{
195-
methodDefinitions[genericArgumentCount] = method;
196-
}
176+
if (luaL_getmetatable(L, DelegateHandleMetatableName).IsNoneOrNil())
177+
{
178+
lua_pop(L, 2);
179+
return false;
197180
}
198181

199-
return methodDefinitions;
182+
var isDelegateHandle = lua_rawequal(L, -1, -2);
183+
lua_pop(L, 2);
184+
return isDelegateHandle;
200185
}
201186

202-
private static bool IsUnsupportedDelegateType(Type type)
203-
{
204-
return type.IsByRef || type.IsByRefLike || type.IsPointer;
205-
}
206187
}

src/Laylua/Library/Marshaler/DefaultLuaMarshaler.TryGetValue.cs

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,9 @@ public override bool TryGetValue<T>(LuaThread thread, int stackIndex, LuaAllowed
242242
{
243243
if ((allowedConversions & LuaAllowedValueConversions.FunctionToCallableTarget) != 0
244244
&& (clrType == typeof(MethodInfo) || clrType.BaseType == typeof(MulticastDelegate))
245-
&& TryGetMethodInfoFromFunction(L, stackIndex, clrType, out var methodResult))
245+
&& TryGetCallableTargetFromFunction(L, stackIndex, clrType, out var callableTarget))
246246
{
247-
obj = (T) methodResult!;
247+
obj = (T) callableTarget!;
248248
return true;
249249
}
250250

@@ -256,30 +256,6 @@ public override bool TryGetValue<T>(LuaThread thread, int stackIndex, LuaAllowed
256256
return true;
257257
}
258258
}
259-
else if ((allowedConversions & LuaAllowedValueConversions.FunctionToCallableTarget) != 0 && clrType.BaseType == typeof(MulticastDelegate))
260-
{
261-
if (TryCreateFunctionReference(thread, stackIndex, out var function))
262-
{
263-
var keepFunctionReference = false;
264-
try
265-
{
266-
var @delegate = CreateDelegateFromFunction(function, clrType);
267-
if (@delegate != null)
268-
{
269-
keepFunctionReference = true;
270-
obj = (T) (object) @delegate;
271-
return true;
272-
}
273-
}
274-
finally
275-
{
276-
if (!keepFunctionReference)
277-
{
278-
function.Dispose();
279-
}
280-
}
281-
}
282-
}
283259
else
284260
{
285261
if (clrType == typeof(LuaWeakReference<LuaFunction>))

0 commit comments

Comments
 (0)