Skip to content

Commit

Permalink
Fixed GetMethodFallback to check GetRuntimeMethods and `TryGetVal…
Browse files Browse the repository at this point in the history
…ueForKey`.
  • Loading branch information
viniciusjarina committed Sep 14, 2019
1 parent c3b15c9 commit 83fb7ad
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 58 deletions.
158 changes: 110 additions & 48 deletions src/Metatables.cs
Expand Up @@ -473,74 +473,141 @@ private int GetMethodFallback
return PushExtensionMethod(luaState, objType, obj, methodName, method);
}
// Try to use get_Item to index into this .net object
var methods = objType.GetMethods();
MethodInfo[] methods = objType.GetMethods();

foreach (var methodInfo in methods)
int res = TryIndexMethods(luaState, methods, obj, index);
if (res != 0)
return res;

// Fallback to GetRuntimeMethods
methods = objType.GetRuntimeMethods().ToArray();

res = TryIndexMethods(luaState, methods, obj, index);
if (res != 0)
return res;

res = TryGetValueForKeyMethods(luaState, methods, obj, index);
if (res != 0)
return res;

// Try find explicity interface implementation
MethodInfo explicitInterfaceMethod = objType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).
FirstOrDefault(m => m.Name == methodName && m.IsPrivate && m.IsVirtual && m.IsFinal);

if (explicitInterfaceMethod != null)
{
if (methodInfo.Name != "get_Item")
var proxyType = new ProxyType(objType);
var methodWrapper = new LuaMethodWrapper(_translator, obj, proxyType, explicitInterfaceMethod);
var invokeDelegate = new LuaNativeFunction(methodWrapper.InvokeFunction);

SetMemberCache(proxyType, methodName, invokeDelegate);

_translator.PushFunction(luaState, invokeDelegate);
_translator.Push(luaState, true);
return 2;
}

return 0;
}

private int TryGetValueForKeyMethods(LuaState luaState, MethodInfo[] methods, object obj, object index)
{
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.Name != "TryGetValueForKey")
continue;

// Check if the signature matches the input
if (methodInfo.GetParameters().Length != 1)
if (methodInfo.GetParameters().Length != 2)
continue;

ParameterInfo[] actualParams = methodInfo.GetParameters();

if (actualParams.Length != 1)
{
_translator.ThrowError(luaState, "method not found (or no indexer): " + index);
luaState.PushNil();
}
else
{
// Get the index in a form acceptable to the getter
index = _translator.GetAsType(luaState, 2, actualParams[0].ParameterType);
// Get the index in a form acceptable to the getter
index = _translator.GetAsType(luaState, 2, actualParams[0].ParameterType);

// If the index type and the parameter doesn't match, just skip it
if (index == null)
break;
// If the index type and the parameter doesn't match, just skip it
if (index == null)
break;

object[] args = new object[1];
object[] args = new object[2];

// Just call the indexer - if out of bounds an exception will happen
args[0] = index;
// Just call the indexer - if out of bounds an exception will happen
args[0] = index;

try
try
{
bool found = (bool)methodInfo.Invoke(obj, args);

if (!found)
{
object result = methodInfo.Invoke(obj, args);
_translator.Push(luaState, result);
_translator.ThrowError(luaState, "key not found: " + index);
luaState.PushNil();
return 1;
}
catch (TargetInvocationException e)
{
// Provide a more readable description for the common case of key not found
if (e.InnerException is KeyNotFoundException)
_translator.ThrowError(luaState, "key '" + index + "' not found ");
else
_translator.ThrowError(luaState, "exception indexing '" + index + "' " + e.Message);

luaState.PushNil();
}
_translator.Push(luaState, args[1]);
return 1;
}
catch (TargetInvocationException e)
{
// Provide a more readable description for the common case of key not found
if (e.InnerException is KeyNotFoundException)
_translator.ThrowError(luaState, "key '" + index + "' not found ");
else
_translator.ThrowError(luaState, "exception indexing '" + index + "' " + e.Message);

luaState.PushNil();
return 1;
}
}
return 0;
}

// Try find explicity interface implementation
MethodInfo explicitInterfaceMethod = objType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).
FirstOrDefault(m => m.Name == methodName && m.IsPrivate && m.IsVirtual && m.IsFinal);

if (explicitInterfaceMethod != null)
private int TryIndexMethods(LuaState luaState, MethodInfo [] methods, object obj, object index)
{
foreach (MethodInfo methodInfo in methods)
{
var proxyType = new ProxyType(objType);
var methodWrapper = new LuaMethodWrapper(_translator, obj, proxyType, explicitInterfaceMethod);
var invokeDelegate = new LuaNativeFunction(methodWrapper.InvokeFunction);
if (methodInfo.Name != "get_Item")
continue;

SetMemberCache(proxyType, methodName, invokeDelegate);
// Check if the signature matches the input
if (methodInfo.GetParameters().Length != 1)
continue;

_translator.PushFunction(luaState, invokeDelegate);
_translator.Push(luaState, true);
return 2;
}
ParameterInfo[] actualParams = methodInfo.GetParameters();

// Get the index in a form acceptable to the getter
index = _translator.GetAsType(luaState, 2, actualParams[0].ParameterType);

// If the index type and the parameter doesn't match, just skip it
if (index == null)
break;

object[] args = new object[1];

// Just call the indexer - if out of bounds an exception will happen
args[0] = index;

try
{
object result = methodInfo.Invoke(obj, args);
_translator.Push(luaState, result);
return 1;
}
catch (TargetInvocationException e)
{
// Provide a more readable description for the common case of key not found
if (e.InnerException is KeyNotFoundException)
_translator.ThrowError(luaState, "key '" + index + "' not found ");
else
_translator.ThrowError(luaState, "exception indexing '" + index + "' " + e.Message);

luaState.PushNil();
return 1;
}
}
return 0;
}

Expand Down Expand Up @@ -913,11 +980,6 @@ private int SetFieldOrPropertyInternal(LuaState luaState)
_translator.ThrowError(luaState, detailMessage); // Pass the original message from trySetMember because it is probably best
}
}
catch (SEHException)
{
// If we are seeing a C++ exception - this must actually be for Lua's private use. Let it handle it
throw;
}
catch (Exception e)
{
ThrowError(luaState, e);
Expand Down
20 changes: 10 additions & 10 deletions tests/src/LuaTests.cs
Expand Up @@ -2573,30 +2573,30 @@ public void CallDictionary()

IDictionary<string,object> obj2 = new Dictionary<string, object>()
{
{ "key1" ,"value1" },
{ "key2" ,"value2" }
{ "key1" ,"value3" },
{ "key2" ,"value4" }
};

lua["obj2"] = obj;
lua["obj2"] = obj2;

lua.DoString("l = obj2.key1");
lua.DoString("m = obj2['key2']");

Assert.AreEqual("value1", lua["l"], "#3");
Assert.AreEqual("value2", lua["m"], "#4");
Assert.AreEqual("value3", lua["l"], "#3");
Assert.AreEqual("value4", lua["m"], "#4");

IDictionary<string, object> obj3 = new System.Dynamic.ExpandoObject();

obj3["key1"] = "value1";
obj3["key2"] = "value2";
obj3["key1"] = "value5";
obj3["key2"] = "value6";

lua["obj3"] = obj;
lua["obj3"] = obj3;

lua.DoString("n = obj3.key1");
lua.DoString("o = obj3['key2']");

Assert.AreEqual("value1", lua["n"], "#5");
Assert.AreEqual("value2", lua["o"], "#6");
Assert.AreEqual("value5", lua["n"], "#5");
Assert.AreEqual("value6", lua["o"], "#6");
}
}

Expand Down

0 comments on commit 83fb7ad

Please sign in to comment.