Unverified Commit 190a8505 authored by Vinicius Jarina's avatar Vinicius Jarina Committed by GitHub
Browse files

Avoid call GetExtensionMethod twice. (#279)

* Avoid call GetExtensionMethod twice.

* `LuaMethodWrapper.Call` cleanup.
parent 10e392a0
...@@ -311,8 +311,7 @@ namespace NLua ...@@ -311,8 +311,7 @@ namespace NLua
{ {
if (!luaState.IsString(stackPos)) if (!luaState.IsString(stackPos))
return null; return null;
string retVal = luaState.ToString(stackPos, false); return luaState.ToString(stackPos, false);
return retVal;
} }
private object GetAsTable(LuaState luaState, int stackPos) private object GetAsTable(LuaState luaState, int stackPos)
...@@ -354,20 +353,20 @@ namespace NLua ...@@ -354,20 +353,20 @@ namespace NLua
{ {
object obj = _translator.GetNetObject(luaState, stackPos); object obj = _translator.GetNetObject(luaState, stackPos);
if (obj == null && luaState.Type(stackPos) == LuaType.Table) if (obj != null || luaState.Type(stackPos) != LuaType.Table)
return obj;
if (luaState.GetMetaField(stackPos, "__index") == LuaType.Nil)
return null;
if (luaState.CheckMetaTable(-1, _translator.Tag))
{ {
if (luaState.GetMetaField(stackPos, "__index") != LuaType.Nil) luaState.Insert(stackPos);
{ luaState.Remove(stackPos + 1);
if (luaState.CheckMetaTable(-1, _translator.Tag)) obj = _translator.GetNetObject(luaState, stackPos);
{
luaState.Insert(stackPos);
luaState.Remove(stackPos + 1);
obj = _translator.GetNetObject(luaState, stackPos);
}
else
luaState.SetTop(-2);
}
} }
else
luaState.SetTop(-2);
return obj; return obj;
} }
......
...@@ -30,6 +30,7 @@ namespace NLua.Extensions ...@@ -30,6 +30,7 @@ namespace NLua.Extensions
luaState.RawGetInteger(LuaRegistry.Index, reference); luaState.RawGetInteger(LuaRegistry.Index, reference);
} }
// ReSharper disable once IdentifierTypo
public static void Unref (this LuaState luaState, int reference) public static void Unref (this LuaState luaState, int reference)
{ {
luaState.Unref(LuaRegistry.Index, reference); luaState.Unref(LuaRegistry.Index, reference);
......
...@@ -47,25 +47,21 @@ namespace NLua ...@@ -47,25 +47,21 @@ namespace NLua
/// <summary> /// <summary>
/// True while a script is being executed /// True while a script is being executed
/// </summary> /// </summary>
public bool IsExecuting => executing; public bool IsExecuting => _executing;
public LuaState State => _luaState; public LuaState State => _luaState;
private ObjectTranslator _translator; private ObjectTranslator _translator;
/// <summary>
/// Used to protect the (global) object translator pool during add/remove
/// </summary>
private static readonly object translatorPoolLock = new object();
/// <summary> /// <summary>
/// Used to ensure multiple .net threads all get serialized by this single lock for access to the lua stack/objects /// Used to ensure multiple .net threads all get serialized by this single lock for access to the lua stack/objects
/// </summary> /// </summary>
//private object luaLock = new object(); //private object luaLock = new object();
private bool _StatePassed; private bool _StatePassed;
private bool executing; private bool _executing;
// The commented code bellow is the initLua, the code assigned here is minified for size/performance reasons. // The commented code bellow is the initLua, the code assigned here is minified for size/performance reasons.
private static string initLuanet = @"local a={}local rawget=rawget;local b=luanet.import_type;local c=luanet.load_assembly;luanet.error,luanet.type=error,type;function a:__index(d)local e=rawget(self,'.fqn')e=(e and e..'.'or'')..d;local f=rawget(luanet,d)or b(e)if f==nil then pcall(c,e)f={['.fqn']=e}setmetatable(f,a)end;rawset(self,d,f)return f end;function a:__call(...)error('No such type: '..rawget(self,'.fqn'),2)end;luanet['.fqn']=false;setmetatable(luanet,a)luanet.load_assembly('mscorlib')"; private const string InitLuanet = @"local a={}local rawget=rawget;local b=luanet.import_type;local c=luanet.load_assembly;luanet.error,luanet.type=error,type;function a:__index(d)local e=rawget(self,'.fqn')e=(e and e..'.'or'')..d;local f=rawget(luanet,d)or b(e)if f==nil then pcall(c,e)f={['.fqn']=e}setmetatable(f,a)end;rawset(self,d,f)return f end;function a:__call(...)error('No such type: '..rawget(self,'.fqn'),2)end;luanet['.fqn']=false;setmetatable(luanet,a)luanet.load_assembly('mscorlib')";
//@"local metatable = {} //@"local metatable = {}
// local rawget = rawget // local rawget = rawget
// local import_type = luanet.import_type // local import_type = luanet.import_type
...@@ -106,7 +102,7 @@ namespace NLua ...@@ -106,7 +102,7 @@ namespace NLua
// -- Preload the mscorlib assembly // -- Preload the mscorlib assembly
// luanet.load_assembly('mscorlib')"; // luanet.load_assembly('mscorlib')";
private static string clr_package = @"if not luanet then require'luanet'end;local a,b=luanet.import_type,luanet.load_assembly;local c={__index=function(d,e)local f=rawget(d,e)if f==nil then f=a(d.packageName.."".""..e)if f==nil then f=a(e)end;d[e]=f end;return f end}function luanet.namespace(g)if type(g)=='table'then local h={}for i=1,#g do h[i]=luanet.namespace(g[i])end;return unpack(h)end;local j={packageName=g}setmetatable(j,c)return j end;local k,l;local function m()l={}k={__index=function(n,e)for i,d in ipairs(l)do local f=d[e]if f then _G[e]=f;return f end end end}setmetatable(_G,k)end;function CLRPackage(o,p)p=p or o;local q=pcall(b,o)return luanet.namespace(p)end;function import(o,p)if not k then m()end;if not p then local i=o:find('%.dll$')if i then p=o:sub(1,i-1)else p=o end end;local j=CLRPackage(o,p)table.insert(l,j)return j end;function luanet.make_array(r,s)local t=r[#s]for i,u in ipairs(s)do t:SetValue(u,i-1)end;return t end;function luanet.each(v)local w=v:GetEnumerator()return function()if w:MoveNext()then return w.Current end end end"; private const string ClrPackage = @"if not luanet then require'luanet'end;local a,b=luanet.import_type,luanet.load_assembly;local c={__index=function(d,e)local f=rawget(d,e)if f==nil then f=a(d.packageName.."".""..e)if f==nil then f=a(e)end;d[e]=f end;return f end}function luanet.namespace(g)if type(g)=='table'then local h={}for i=1,#g do h[i]=luanet.namespace(g[i])end;return unpack(h)end;local j={packageName=g}setmetatable(j,c)return j end;local k,l;local function m()l={}k={__index=function(n,e)for i,d in ipairs(l)do local f=d[e]if f then _G[e]=f;return f end end end}setmetatable(_G,k)end;function CLRPackage(o,p)p=p or o;local q=pcall(b,o)return luanet.namespace(p)end;function import(o,p)if not k then m()end;if not p then local i=o:find('%.dll$')if i then p=o:sub(1,i-1)else p=o end end;local j=CLRPackage(o,p)table.insert(l,j)return j end;function luanet.make_array(r,s)local t=r[#s]for i,u in ipairs(s)do t:SetValue(u,i-1)end;return t end;function luanet.each(v)local w=v:GetEnumerator()return function()if w:MoveNext()then return w.Current end end end";
//@"--- //@"---
//--- This lua module provides auto importing of .net classes into a named package. //--- This lua module provides auto importing of .net classes into a named package.
//--- Makes for super easy use of LuaInterface glue //--- Makes for super easy use of LuaInterface glue
...@@ -282,12 +278,11 @@ namespace NLua ...@@ -282,12 +278,11 @@ namespace NLua
_luaState.SetTable(-3); _luaState.SetTable(-3);
_luaState.PopGlobalTable(); _luaState.PopGlobalTable();
_translator = new ObjectTranslator(this, _luaState); _translator = new ObjectTranslator(this, _luaState);
lock (translatorPoolLock)
{ ObjectTranslatorPool.Instance.Add(_luaState, _translator);
ObjectTranslatorPool.Instance.Add(_luaState, _translator);
}
_luaState.PopGlobalTable(); _luaState.PopGlobalTable();
_luaState.DoString(initLuanet); _luaState.DoString(InitLuanet);
} }
public void Close() public void Close()
...@@ -295,12 +290,9 @@ namespace NLua ...@@ -295,12 +290,9 @@ namespace NLua
if (_StatePassed || _luaState == null) if (_StatePassed || _luaState == null)
return; return;
lock (translatorPoolLock) _luaState.Close();
{ ObjectTranslatorPool.Instance.Remove(_luaState);
_luaState.Close(); _luaState = null;
ObjectTranslatorPool.Instance.Remove(_luaState);
_luaState = null;
}
} }
#if __IOS__ || __TVOS__ || __WATCHOS__ #if __IOS__ || __TVOS__ || __WATCHOS__
...@@ -388,7 +380,7 @@ namespace NLua ...@@ -388,7 +380,7 @@ namespace NLua
public LuaFunction LoadString(string chunk, string name) public LuaFunction LoadString(string chunk, string name)
{ {
int oldTop = _luaState.GetTop(); int oldTop = _luaState.GetTop();
executing = true; _executing = true;
try try
{ {
...@@ -397,7 +389,7 @@ namespace NLua ...@@ -397,7 +389,7 @@ namespace NLua
} }
finally finally
{ {
executing = false; _executing = false;
} }
var result = _translator.GetFunction(_luaState, -1); var result = _translator.GetFunction(_luaState, -1);
...@@ -414,7 +406,7 @@ namespace NLua ...@@ -414,7 +406,7 @@ namespace NLua
public LuaFunction LoadString(byte[] chunk, string name) public LuaFunction LoadString(byte[] chunk, string name)
{ {
int oldTop = _luaState.GetTop(); int oldTop = _luaState.GetTop();
executing = true; _executing = true;
try try
{ {
...@@ -423,7 +415,7 @@ namespace NLua ...@@ -423,7 +415,7 @@ namespace NLua
} }
finally finally
{ {
executing = false; _executing = false;
} }
var result = _translator.GetFunction(_luaState, -1); var result = _translator.GetFunction(_luaState, -1);
...@@ -457,7 +449,7 @@ namespace NLua ...@@ -457,7 +449,7 @@ namespace NLua
public object[] DoString(byte[] chunk, string chunkName = "chunk") public object[] DoString(byte[] chunk, string chunkName = "chunk")
{ {
int oldTop = _luaState.GetTop(); int oldTop = _luaState.GetTop();
executing = true; _executing = true;
if (_luaState.LoadBuffer(chunk, chunkName) != LuaStatus.OK) if (_luaState.LoadBuffer(chunk, chunkName) != LuaStatus.OK)
ThrowExceptionFromError(oldTop); ThrowExceptionFromError(oldTop);
...@@ -479,7 +471,7 @@ namespace NLua ...@@ -479,7 +471,7 @@ namespace NLua
} }
finally finally
{ {
executing = false; _executing = false;
} }
} }
...@@ -492,7 +484,7 @@ namespace NLua ...@@ -492,7 +484,7 @@ namespace NLua
public object[] DoString(string chunk, string chunkName = "chunk") public object[] DoString(string chunk, string chunkName = "chunk")
{ {
int oldTop = _luaState.GetTop(); int oldTop = _luaState.GetTop();
executing = true; _executing = true;
if (_luaState.LoadString(chunk, chunkName) != LuaStatus.OK) if (_luaState.LoadString(chunk, chunkName) != LuaStatus.OK)
ThrowExceptionFromError(oldTop); ThrowExceptionFromError(oldTop);
...@@ -514,7 +506,7 @@ namespace NLua ...@@ -514,7 +506,7 @@ namespace NLua
} }
finally finally
{ {
executing = false; _executing = false;
} }
} }
...@@ -529,7 +521,7 @@ namespace NLua ...@@ -529,7 +521,7 @@ namespace NLua
if (_luaState.LoadFile(fileName) != LuaStatus.OK) if (_luaState.LoadFile(fileName) != LuaStatus.OK)
ThrowExceptionFromError(oldTop); ThrowExceptionFromError(oldTop);
executing = true; _executing = true;
int errorFunctionIndex = 0; int errorFunctionIndex = 0;
if (UseTraceback) if (UseTraceback)
...@@ -547,7 +539,7 @@ namespace NLua ...@@ -547,7 +539,7 @@ namespace NLua
} }
finally finally
{ {
executing = false; _executing = false;
} }
} }
...@@ -776,9 +768,10 @@ namespace NLua ...@@ -776,9 +768,10 @@ namespace NLua
CodeGeneration.Instance.RegisterLuaClassType(klass, luaClass); CodeGeneration.Instance.RegisterLuaClassType(klass, luaClass);
} }
// ReSharper disable once InconsistentNaming
public void LoadCLRPackage() public void LoadCLRPackage()
{ {
_luaState.DoString(clr_package); _luaState.DoString(ClrPackage);
} }
/* /*
* Gets a function global variable as a delegate of * Gets a function global variable as a delegate of
...@@ -821,7 +814,7 @@ namespace NLua ...@@ -821,7 +814,7 @@ namespace NLua
_translator.Push(_luaState, args[i]); _translator.Push(_luaState, args[i]);
} }
executing = true; _executing = true;
try try
{ {
...@@ -838,7 +831,7 @@ namespace NLua ...@@ -838,7 +831,7 @@ namespace NLua
} }
finally finally
{ {
executing = false; _executing = false;
} }
if (returnTypes != null) if (returnTypes != null)
...@@ -934,7 +927,7 @@ namespace NLua ...@@ -934,7 +927,7 @@ namespace NLua
{ {
if (_hookCallback == null) if (_hookCallback == null)
{ {
_hookCallback = new LuaHookFunction(Lua.DebugHookCallback); _hookCallback = DebugHookCallback;
_luaState.SetHook(_hookCallback, mask, count); _luaState.SetHook(_hookCallback, mask, count);
} }
...@@ -1164,7 +1157,7 @@ namespace NLua ...@@ -1164,7 +1157,7 @@ namespace NLua
_luaState.SetTop(oldTop); _luaState.SetTop(oldTop);
} }
public LuaFunction RegisterFunction(string path, MethodBase function /*MethodInfo function*/) public LuaFunction RegisterFunction(string path, MethodBase function)
{ {
return RegisterFunction(path, null, function); return RegisterFunction(path, null, function);
} }
...@@ -1173,7 +1166,7 @@ namespace NLua ...@@ -1173,7 +1166,7 @@ namespace NLua
* Registers an object's method as a Lua function (global or table field) * Registers an object's method as a Lua function (global or table field)
* The method may have any signature * The method may have any signature
*/ */
public LuaFunction RegisterFunction(string path, object target, MethodBase function /*MethodInfo function*/) //CP: Fix for struct constructor by Alexander Kappner (link: http://luaforge.net/forum/forum.php?thread_id = 2859&forum_id = 145) public LuaFunction RegisterFunction(string path, object target, MethodBase function)
{ {
// We leave nothing on the stack when we are done // We leave nothing on the stack when we are done
int oldTop = _luaState.GetTop(); int oldTop = _luaState.GetTop();
...@@ -1198,6 +1191,7 @@ namespace NLua ...@@ -1198,6 +1191,7 @@ namespace NLua
return equal; return equal;
} }
// ReSharper disable once InconsistentNaming
internal void PushCSFunction(LuaNativeFunction function) internal void PushCSFunction(LuaNativeFunction function)
{ {
_translator.PushFunction(_luaState, function); _translator.PushFunction(_luaState, function);
......
...@@ -43,33 +43,32 @@ namespace NLua ...@@ -43,33 +43,32 @@ namespace NLua
public LuaNativeFunction LessThanFunction { get; } public LuaNativeFunction LessThanFunction { get; }
public LuaNativeFunction LessThanOrEqualFunction { get; } public LuaNativeFunction LessThanOrEqualFunction { get; }
readonly Dictionary<object, Dictionary<object, object>> memberCache = new Dictionary<object, Dictionary<object, object>>(); readonly Dictionary<object, Dictionary<object, object>> _memberCache = new Dictionary<object, Dictionary<object, object>>();
readonly ObjectTranslator translator; readonly ObjectTranslator _translator;
/* /*
* __index metafunction for CLR objects. Implemented in Lua. * __index metafunction for CLR objects. Implemented in Lua.
*/ */
static readonly string luaIndexFunction = public const string LuaIndexFunction = @"local function a(b,c)local d=getmetatable(b)local e=d.cache[c]if e~=nil then return e else local f,g=get_object_member(b,c)if g then d.cache[c]=f end;return f end end;return a";
@"local function index(obj,name) //@"local function index(obj,name)
local meta = getmetatable(obj) // local meta = getmetatable(obj)
local cached = meta.cache[name] // local cached = meta.cache[name]
if cached ~= nil then // if cached ~= nil then
return cached // return cached
else // else
local value,isFunc = get_object_member(obj,name) // local value,isFunc = get_object_member(obj,name)
if isFunc then // if isFunc then
meta.cache[name]=value // meta.cache[name]=value
end // end
return value // return value
end // end
end //end
return index"; //return index";
public static string LuaIndexFunction => luaIndexFunction;
public MetaFunctions(ObjectTranslator translator) public MetaFunctions(ObjectTranslator translator)
{ {
this.translator = translator; _translator = translator;
GcFunction = CollectObject; GcFunction = CollectObject;
ToStringFunction = ToStringLua; ToStringFunction = ToStringLua;
IndexFunction = GetMethod; IndexFunction = GetMethod;
...@@ -345,115 +344,126 @@ namespace NLua ...@@ -345,115 +344,126 @@ namespace NLua
private int GetMethodInternal(LuaState luaState) private int GetMethodInternal(LuaState luaState)
{ {
object obj = translator.GetRawNetObject(luaState, 1); object obj = _translator.GetRawNetObject(luaState, 1);
if (obj == null) if (obj == null)
{ {
translator.ThrowError(luaState, "Trying to index an invalid object reference"); _translator.ThrowError(luaState, "Trying to index an invalid object reference");
luaState.PushNil(); luaState.PushNil();
return 1; return 1;
} }
object index = translator.GetObject(luaState, 2); object index = _translator.GetObject(luaState, 2);
string methodName = index as string; // will be null if not a string arg string methodName = index as string; // will be null if not a string arg
var objType = obj.GetType(); var objType = obj.GetType();
var proxyType = new ProxyType(objType); var proxyType = new ProxyType(objType);
// Handle the most common case, looking up the method by name. // Handle the most common case, looking up the method by name.
// CP: This will fail when using indexers and attempting to get a value with the same name as a property of the object, // CP: This will fail when using indexers and attempting to get a value with the same name as a property of the object,
// ie: xmlelement['item'] <- item is a property of xmlelement // ie: xmlelement['item'] <- item is a property of xmlelement
try
{
if (!string.IsNullOrEmpty(methodName) && IsMemberPresent(proxyType, methodName))
return GetMember(luaState, proxyType, obj, methodName, BindingFlags.Instance);
}
catch
{
Debug.WriteLine("[Exception] Fail to fetch Member: {0}", methodName);
}
if (!string.IsNullOrEmpty(methodName) && IsMemberPresent(proxyType, methodName))
return GetMember(luaState, proxyType, obj, methodName, BindingFlags.Instance);
int fallback = GetMethodFallback(luaState, objType, obj, index, methodName);
if (fallback != 0)
return fallback;
luaState.PushBoolean(false);
return 2;
}
private int GetMethodFallback
(LuaState luaState,
Type objType,
object obj,
object index,
string methodName)
{
// Try to access by array if the type is right and index is an int (lua numbers always come across as double) // Try to access by array if the type is right and index is an int (lua numbers always come across as double)
if (objType.IsArray && index is double) if (objType.IsArray && index is double)
{ {
int intIndex = (int)((double)index); int intIndex = (int)(double)index;
Type type = objType.UnderlyingSystemType; Type type = objType.UnderlyingSystemType;
if (type == typeof(float[])) if (type == typeof(float[]))
{ {
float[] arr = (float[])obj; float[] arr = (float[])obj;
translator.Push(luaState, arr[intIndex]); _translator.Push(luaState, arr[intIndex]);
return 0;
} }
else if (type == typeof(double[])) if (type == typeof(double[]))
{ {
double[] arr = (double[])obj; double[] arr = (double[])obj;
translator.Push(luaState, arr[intIndex]); _translator.Push(luaState, arr[intIndex]);
return 0;
} }
else if (type == typeof(int[])) if (type == typeof(int[]))
{ {
int[] arr = (int[])obj; int[] arr = (int[])obj;
translator.Push(luaState, arr[intIndex]); _translator.Push(luaState, arr[intIndex]);
} return 0;
else
{
object[] arr = (object[])obj;
translator.Push(luaState, arr[intIndex]);
} }
object[] arrObj = (object[])obj;
_translator.Push(luaState, arrObj[intIndex]);
return 0;
} }
else
object method;
if (!string.IsNullOrEmpty(methodName) && TryGetExtensionMethod(objType, methodName, out method))
{ {
if (!string.IsNullOrEmpty(methodName) && IsExtensionMethodPresent(objType, methodName)) return PushExtensionMethod(luaState, objType, obj, methodName, method);
}
// Try to use get_Item to index into this .net object
var methods = objType.GetMethods();
foreach (var methodInfo in methods)
{
if (methodInfo.Name != "get_Item")
continue;
// Check if the signature matches the input
if (methodInfo.GetParameters().Length != 1)
continue;
var actualParams = methodInfo.GetParameters();
if (actualParams.Length != 1)
{ {
return GetExtensionMethod(luaState, objType, obj, methodName); _translator.ThrowError(luaState, "method not found (or no indexer): " + index);
luaState.PushNil();
} }
// Try to use get_Item to index into this .net object else
var methods = objType.GetMethods();
foreach (var methodInfo in methods)
{ {
if (methodInfo.Name != "get_Item") // Get the index in a form acceptable to the getter
continue; index = _translator.GetAsType(luaState, 2, actualParams[0].ParameterType);
object[] args = new object[1];
// Check if the signature matches the input
if (methodInfo.GetParameters().Length != 1)
continue;
var actualParams = methodInfo.GetParameters(); // Just call the indexer - if out of bounds an exception will happen
args[0] = index;
if (actualParams.Length != 1) try
{ {
translator.ThrowError(luaState, "method not found (or no indexer): " + index); object result = methodInfo.Invoke(obj, args);
luaState.PushNil(); _translator.Push(luaState, result);
} }
else catch (TargetInvocationException e)
{ {
// Get the index in a form acceptable to the getter // Provide a more readable description for the common case of key not found
index = translator.GetAsType(luaState, 2, actualParams[0].ParameterType); if (e.InnerException is KeyNotFoundException)
object[] args = new object[1]; _translator.ThrowError(luaState, "key '" + index + "' not found ");
else
_translator.ThrowError(luaState, "exception indexing '" + index + "' " + e.Message);
// Just call the indexer - if out of bounds an exception will happen luaState.PushNil();
args[0] = index;
try
{
object result = methodInfo.Invoke(obj, args);
translator.Push(luaState, result);
}
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();
}
} }
} }
} }
luaState.PushBoolean(false);
return 2; return 0;
} }
/* /*
...@@ -473,11 +483,11 @@ namespace NLua ...@@ -473,11 +483,11 @@ namespace NLua
private int GetBaseMethodInternal(LuaState luaState) private int GetBaseMethodInternal(LuaState luaState)
{ {
object obj = translator.GetRawNetObject(luaState, 1); object obj = _translator.GetRawNetObject(luaState, 1);
if (obj == null) if (obj == null)
{ {
translator.ThrowError(luaState, "Trying to index an invalid object reference"); _translator.ThrowError(luaState, "Trying to index an invalid object reference");
luaState.PushNil(); luaState.PushNil();
luaState.PushBoolean(false); luaState.PushBoolean(false);
return 2; return 2;
...@@ -522,35 +532,41 @@ namespace NLua ...@@ -522,35 +532,41 @@ namespace NLua
return members.Length > 0; return members.Length > 0;
} }
bool IsExtensionMethodPresent(Type type, string name) bool TryGetExtensionMethod(Type type, string name, out object method)
{ {
object cachedMember = CheckMemberCache(type, name); object cachedMember = CheckMemberCache(type, name);
if (cachedMember != null) if (cachedMember != null)
{
method = cachedMember;
return true; return true;
}
return translator.IsExtensionMethodPresent(type, name); MethodInfo methodInfo;
bool found = _translator.TryGetExtensionMethod(type, name, out methodInfo);
method = methodInfo;
return found;
} }
int GetExtensionMethod(LuaState luaState, Type type, object obj, string name) int PushExtensionMethod(LuaState luaState, Type type, object obj, string name, object method)
{ {
var cachedMember = CheckMemberCache(type, name) as LuaNativeFunction; var cachedMember = method as LuaNativeFunction;
if (cachedMember != null) if (cachedMember != null)
{ {
translator.PushFunction(luaState, cachedMember); _translator.PushFunction(luaState, cachedMember);
translator.Push(luaState, true); _translator.Push(luaState, true);
return 2; return 2;
} }
MethodInfo methodInfo = translator.GetExtensionMethod(type, name); var methodInfo = (MethodInfo)method;
var methodWrapper = new LuaMethodWrapper(translator, obj, new ProxyType(type), methodInfo); var methodWrapper = new LuaMethodWrapper(_translator, obj, new ProxyType(type), methodInfo);
var invokeDelegate = new LuaNativeFunction(methodWrapper.InvokeFunction); var invokeDelegate = new LuaNativeFunction(methodWrapper.InvokeFunction);
SetMemberCache(type, name, invokeDelegate); SetMemberCache(type, name, invokeDelegate);
translator.PushFunction(luaState, invokeDelegate); _translator.PushFunction(luaState, invokeDelegate);
translator.Push(luaState, true); _translator.Push(luaState, true);
return 2; return 2;
} }
...@@ -568,8 +584,8 @@ namespace NLua ...@@ -568,8 +584,8 @@ namespace NLua
if (cachedMember is LuaNativeFunction) if (cachedMember is LuaNativeFunction)
{ {
translator.PushFunction(luaState, (LuaNativeFunction)cachedMember); _translator.PushFunction(luaState, (LuaNativeFunction)cachedMember);
translator.Push(luaState, true); _translator.Push(luaState, true);
return 2; return 2;
} }
if (cachedMember != null) if (cachedMember != null)
...@@ -605,7 +621,7 @@ namespace NLua ...@@ -605,7 +621,7 @@ namespace NLua
try try
{ {
var value = field.GetValue(obj); var value = field.GetValue(obj);
translator.Push(luaState, value); _translator.Push(luaState, value);
} }
catch catch
{ {
...@@ -622,7 +638,7 @@ namespace NLua ...@@ -622,7 +638,7 @@ namespace NLua
try try
{ {
object value = property.GetValue(obj, null); object value = property.GetValue(obj, null);
translator.Push(luaState, value); _translator.Push(luaState, value);
} }
catch (ArgumentException) catch (ArgumentException)
{ {
...@@ -644,42 +660,42 @@ namespace NLua ...@@ -644,42 +660,42 @@ namespace NLua
if (cachedMember == null) if (cachedMember == null)
SetMemberCache(objType, methodName, member); SetMemberCache(objType, methodName, member);
translator.Push(luaState, new RegisterEventHandler(translator.PendingEvents, obj, eventInfo)); _translator.Push(luaState, new RegisterEventHandler(_translator.PendingEvents, obj, eventInfo));
} }
else if (!implicitStatic) else if (!implicitStatic)
{ {
if (member.MemberType == MemberTypes.NestedType) if (member.MemberType == MemberTypes.NestedType && member.DeclaringType != null)
{ {
if (cachedMember == null) if (cachedMember == null)
SetMemberCache(objType, methodName, member); SetMemberCache(objType, methodName, member);
// Find the name of our class // Find the name of our class
string name = member.Name; string name = member.Name;
var dectype = member.DeclaringType; Type decType = member.DeclaringType;
// Build a new long name and try to find the type by name // Build a new long name and try to find the type by name
string longname = dectype.FullName + "+" + name; string longName = decType.FullName + "+" + name;
var nestedType = translator.FindType(longname); var nestedType = _translator.FindType(longName);
translator.PushType(luaState, nestedType); _translator.PushType(luaState, nestedType);
} }
else else
{ {
// Member type must be 'method' // Member type must be 'method'
var methodWrapper = new LuaMethodWrapper(translator, objType, methodName, bindingType); var methodWrapper = new LuaMethodWrapper(_translator, objType, methodName, bindingType);
var wrapper = methodWrapper.InvokeFunction; var wrapper = methodWrapper.InvokeFunction;
if (cachedMember == null) if (cachedMember == null)
SetMemberCache(objType, methodName, wrapper); SetMemberCache(objType, methodName, wrapper);
translator.PushFunction(luaState, wrapper); _translator.PushFunction(luaState, wrapper);
translator.Push(luaState, true); _translator.Push(luaState, true);
return 2; return 2;
} }
} }
else else
{ {
// If we reach this point we found a static method, but can't use it in this context because the user passed in an instance // If we reach this point we found a static method, but can't use it in this context because the user passed in an instance
translator.ThrowError(luaState, "Can't pass instance to static method " + methodName); _translator.ThrowError(luaState, "Can't pass instance to static method " + methodName);
luaState.PushNil(); luaState.PushNil();
} }
} }
...@@ -691,12 +707,12 @@ namespace NLua ...@@ -691,12 +707,12 @@ namespace NLua
// We want to throw an exception because merely returning 'nil' in this case // We want to throw an exception because merely returning 'nil' in this case
// is not sufficient. valid data members may return nil and therefore there must be some // is not sufficient. valid data members may return nil and therefore there must be some
// way to know the member just doesn't exist. // way to know the member just doesn't exist.
translator.ThrowError(luaState, "Unknown member name " + methodName); _translator.ThrowError(luaState, "Unknown member name " + methodName);
luaState.PushNil(); luaState.PushNil();
} }
// Push false because we are NOT returning a function (see luaIndexFunction) // Push false because we are NOT returning a function (see luaIndexFunction)
translator.Push(luaState, false); _translator.Push(luaState, false);
return 2; return 2;
} }
...@@ -712,7 +728,7 @@ namespace NLua ...@@ -712,7 +728,7 @@ namespace NLua
{ {
Dictionary<object, object> members; Dictionary<object, object> members;
if (!memberCache.TryGetValue(objType, out members)) if (!_memberCache.TryGetValue(objType, out members))
return null; return null;
object memberValue; object memberValue;
...@@ -736,14 +752,14 @@ namespace NLua ...@@ -736,14 +752,14 @@ namespace NLua
Dictionary<object, object> members; Dictionary<object, object> members;
Dictionary<object, object> memberCacheValue; Dictionary<object, object> memberCacheValue;
if (memberCache.TryGetValue(objType, out memberCacheValue)) if (_memberCache.TryGetValue(objType, out memberCacheValue))
{ {
members = memberCacheValue; members = memberCacheValue;
} }
else else
{ {
members = new Dictionary<object, object>(); members = new Dictionary<object, object>();
memberCache[objType] = members; _memberCache[objType] = members;
} }
members[memberName] = member; members[memberName] = member;
...@@ -767,11 +783,11 @@ namespace NLua ...@@ -767,11 +783,11 @@ namespace NLua
private int SetFieldOrPropertyInternal(LuaState luaState) private int SetFieldOrPropertyInternal(LuaState luaState)
{ {
object target = translator.GetRawNetObject(luaState, 1); object target = _translator.GetRawNetObject(luaState, 1);
if (target == null) if (target == null)
{ {
translator.ThrowError(luaState, "trying to index and invalid object reference"); _translator.ThrowError(luaState, "trying to index and invalid object reference");
return 0; return 0;
} }
...@@ -791,7 +807,7 @@ namespace NLua ...@@ -791,7 +807,7 @@ namespace NLua
{ {
int index = (int)luaState.ToNumber(2); int index = (int)luaState.ToNumber(2);
var arr = (Array)target; var arr = (Array)target;
object val = translator.GetAsType(luaState, 3, arr.GetType().GetElementType()); object val = _translator.GetAsType(luaState, 3, arr.GetType().GetElementType());
arr.SetValue(val, index); arr.SetValue(val, index);
} }
else else
...@@ -804,9 +820,9 @@ namespace NLua ...@@ -804,9 +820,9 @@ namespace NLua
var valueType = args[1].ParameterType; var valueType = args[1].ParameterType;
// The new value the user specified // The new value the user specified
object val = translator.GetAsType(luaState, 3, valueType); object val = _translator.GetAsType(luaState, 3, valueType);
var indexType = args[0].ParameterType; var indexType = args[0].ParameterType;
object index = translator.GetAsType(luaState, 2, indexType); object index = _translator.GetAsType(luaState, 2, indexType);
object[] methodArgs = new object[2]; object[] methodArgs = new object[2];
...@@ -816,7 +832,7 @@ namespace NLua ...@@ -816,7 +832,7 @@ namespace NLua
setter.Invoke(target, methodArgs); setter.Invoke(target, methodArgs);
} }
else else
translator.ThrowError(luaState, detailMessage); // Pass the original message from trySetMember because it is probably best _translator.ThrowError(luaState, detailMessage); // Pass the original message from trySetMember because it is probably best
} }
} }
catch (SEHException) catch (SEHException)
...@@ -881,7 +897,7 @@ namespace NLua ...@@ -881,7 +897,7 @@ namespace NLua
if (member.MemberType == MemberTypes.Field) if (member.MemberType == MemberTypes.Field)
{ {
var field = (FieldInfo)member; var field = (FieldInfo)member;
object val = translator.GetAsType(luaState, 3, field.FieldType); object val = _translator.GetAsType(luaState, 3, field.FieldType);
try try
{ {
...@@ -897,7 +913,7 @@ namespace NLua ...@@ -897,7 +913,7 @@ namespace NLua
if (member.MemberType == MemberTypes.Property) if (member.MemberType == MemberTypes.Property)
{ {
var property = (PropertyInfo)member; var property = (PropertyInfo)member;
object val = translator.GetAsType(luaState, 3, property.PropertyType); object val = _translator.GetAsType(luaState, 3, property.PropertyType);
try try
{ {
...@@ -925,7 +941,7 @@ namespace NLua ...@@ -925,7 +941,7 @@ namespace NLua
bool success = TrySetMember(luaState, targetType, target, bindingType, out detail); bool success = TrySetMember(luaState, targetType, target, bindingType, out detail);
if (!success) if (!success)
translator.ThrowError(luaState, detail); _translator.ThrowError(luaState, detail);
return 0; return 0;
} }
...@@ -934,6 +950,7 @@ namespace NLua ...@@ -934,6 +950,7 @@ namespace NLua
/// Convert a C# exception into a Lua error /// Convert a C# exception into a Lua error
/// </summary> /// </summary>
/// <param name="e"></param> /// <param name="e"></param>
/// <param name="luaState"></param>
/// We try to look into the exception to give the most meaningful description /// We try to look into the exception to give the most meaningful description
void ThrowError(LuaState luaState, Exception e) void ThrowError(LuaState luaState, Exception e)
{ {
...@@ -943,7 +960,7 @@ namespace NLua ...@@ -943,7 +960,7 @@ namespace NLua
if (te != null) if (te != null)
e = te.InnerException; e = te.InnerException;
translator.ThrowError(luaState, e); _translator.ThrowError(luaState, e);
} }
/* /*
...@@ -962,11 +979,11 @@ namespace NLua ...@@ -962,11 +979,11 @@ namespace NLua
private int GetClassMethodInternal(LuaState luaState) private int GetClassMethodInternal(LuaState luaState)
{ {
var klass = translator.GetRawNetObject(luaState, 1) as ProxyType; var klass = _translator.GetRawNetObject(luaState, 1) as ProxyType;
if (klass == null) if (klass == null)
{ {
translator.ThrowError(luaState, "Trying to index an invalid type reference"); _translator.ThrowError(luaState, "Trying to index an invalid type reference");
luaState.PushNil(); luaState.PushNil();
return 1; return 1;
} }
...@@ -974,7 +991,7 @@ namespace NLua ...@@ -974,7 +991,7 @@ namespace NLua
if (luaState.IsNumber(2)) if (luaState.IsNumber(2))
{ {
int size = (int)luaState.ToNumber(2); int size = (int)luaState.ToNumber(2);
translator.Push(luaState, Array.CreateInstance(klass.UnderlyingSystemType, size)); _translator.Push(luaState, Array.CreateInstance(klass.UnderlyingSystemType, size));
return 1; return 1;
} }
...@@ -1004,11 +1021,11 @@ namespace NLua ...@@ -1004,11 +1021,11 @@ namespace NLua
private int SetClassFieldOrPropertyInternal(LuaState luaState) private int SetClassFieldOrPropertyInternal(LuaState luaState)
{ {
var target = translator.GetRawNetObject(luaState, 1) as ProxyType; var target = _translator.GetRawNetObject(luaState, 1) as ProxyType;
if (target == null) if (target == null)
{ {
translator.ThrowError(luaState, "trying to index an invalid type reference"); _translator.ThrowError(luaState, "trying to index an invalid type reference");
return 0; return 0;
} }
...@@ -1031,11 +1048,11 @@ namespace NLua ...@@ -1031,11 +1048,11 @@ namespace NLua
int CallDelegateInternal(LuaState luaState) int CallDelegateInternal(LuaState luaState)
{ {
var del = translator.GetRawNetObject(luaState, 1) as Delegate; var del = _translator.GetRawNetObject(luaState, 1) as Delegate;
if (del == null) if (del == null)
{ {
translator.ThrowError(luaState, "Trying to invoke a not delegate or callable value"); _translator.ThrowError(luaState, "Trying to invoke a not delegate or callable value");
luaState.PushNil(); luaState.PushNil();
return 1; return 1;
} }
...@@ -1044,7 +1061,7 @@ namespace NLua ...@@ -1044,7 +1061,7 @@ namespace NLua
var validDelegate = new MethodCache(); var validDelegate = new MethodCache();
MethodBase methodDelegate = del.Method; MethodBase methodDelegate = del.Method;
bool isOk = MatchParameters(luaState, methodDelegate, ref validDelegate); bool isOk = MatchParameters(luaState, methodDelegate, validDelegate, 0);
if (isOk) if (isOk)
{ {
...@@ -1055,11 +1072,11 @@ namespace NLua ...@@ -1055,11 +1072,11 @@ namespace NLua
else else
result = methodDelegate.Invoke(del.Target, validDelegate.args); result = methodDelegate.Invoke(del.Target, validDelegate.args);
translator.Push(luaState, result); _translator.Push(luaState, result);
return 1; return 1;
} }
translator.ThrowError(luaState, "Cannot invoke delegate (invalid arguments for " + methodDelegate.Name + ")"); _translator.ThrowError(luaState, "Cannot invoke delegate (invalid arguments for " + methodDelegate.Name + ")");
luaState.PushNil(); luaState.PushNil();
return 1; return 1;
} }
...@@ -1083,11 +1100,11 @@ namespace NLua ...@@ -1083,11 +1100,11 @@ namespace NLua
private int CallConstructorInternal(LuaState luaState) private int CallConstructorInternal(LuaState luaState)
{ {
var klass = translator.GetRawNetObject(luaState, 1) as ProxyType; var klass = _translator.GetRawNetObject(luaState, 1) as ProxyType;
if (klass == null) if (klass == null)
{ {
translator.ThrowError(luaState, "Trying to call constructor on an invalid type reference"); _translator.ThrowError(luaState, "Trying to call constructor on an invalid type reference");
luaState.PushNil(); luaState.PushNil();
return 1; return 1;
} }
...@@ -1099,14 +1116,14 @@ namespace NLua ...@@ -1099,14 +1116,14 @@ namespace NLua
foreach (var constructor in constructors) foreach (var constructor in constructors)
{ {
bool isConstructor = MatchParameters(luaState, constructor, ref validConstructor); bool isConstructor = MatchParameters(luaState, constructor, validConstructor, 0);
if (!isConstructor) if (!isConstructor)
continue; continue;
try try
{ {
translator.Push(luaState, constructor.Invoke(validConstructor.args)); _translator.Push(luaState, constructor.Invoke(validConstructor.args));
} }
catch (TargetInvocationException e) catch (TargetInvocationException e)
{ {
...@@ -1125,13 +1142,13 @@ namespace NLua ...@@ -1125,13 +1142,13 @@ namespace NLua
int numLuaParams = luaState.GetTop(); int numLuaParams = luaState.GetTop();
if (numLuaParams == 0) if (numLuaParams == 0)
{ {
translator.Push(luaState, Activator.CreateInstance(klass.UnderlyingSystemType)); _translator.Push(luaState, Activator.CreateInstance(klass.UnderlyingSystemType));
return 1; return 1;
} }
} }
string constructorName = constructors.Length == 0 ? "unknown" : constructors[0].Name; string constructorName = constructors.Length == 0 ? "unknown" : constructors[0].Name;
translator.ThrowError(luaState, string.Format("{0} does not contain constructor({1}) argument match", _translator.ThrowError(luaState, string.Format("{0} does not contain constructor({1}) argument match",
klass.UnderlyingSystemType, constructorName)); klass.UnderlyingSystemType, constructorName));
luaState.PushNil(); luaState.PushNil();
return 1; return 1;
...@@ -1180,7 +1197,7 @@ namespace NLua ...@@ -1180,7 +1197,7 @@ namespace NLua
foreach (var op in operators) foreach (var op in operators)
{ {
bool isOk = translator.MatchParameters(luaState, op, ref validOperator); bool isOk = translator.MatchParameters(luaState, op, validOperator, 0);
if (!isOk) if (!isOk)
continue; continue;
...@@ -1256,13 +1273,11 @@ namespace NLua ...@@ -1256,13 +1273,11 @@ namespace NLua
* if the match was successful. It it was also returns the information * if the match was successful. It it was also returns the information
* necessary to invoke the method. * necessary to invoke the method.
*/ */
internal bool MatchParameters(LuaState luaState, MethodBase method, ref MethodCache methodCache) internal bool MatchParameters(LuaState luaState, MethodBase method, MethodCache methodCache, int skipParam)
{ {
ExtractValue extractValue;
bool isMethod = true;
var paramInfo = method.GetParameters(); var paramInfo = method.GetParameters();
int currentLuaParam = 1; int currentLuaParam = 1;
int nLuaParams = luaState.GetTop(); int nLuaParams = luaState.GetTop() - skipParam;
var paramList = new List<object>(); var paramList = new List<object>();
var outList = new List<int>(); var outList = new List<int>();
var argTypes = new List<MethodArgs>(); var argTypes = new List<MethodArgs>();
...@@ -1272,10 +1287,12 @@ namespace NLua ...@@ -1272,10 +1287,12 @@ namespace NLua
if (!currentNetParam.IsIn && currentNetParam.IsOut) // Skips out params if (!currentNetParam.IsIn && currentNetParam.IsOut) // Skips out params
{ {
paramList.Add(null); paramList.Add(null);
outList.Add(paramList.LastIndexOf(null)); outList.Add(paramList.Count - 1);
continue;
} // Type does not match, ignore if the parameter is optional } // Type does not match, ignore if the parameter is optional
else if (IsParamsArray(luaState, nLuaParams, currentLuaParam, currentNetParam, out extractValue))
ExtractValue extractValue;
if (IsParamsArray(luaState, nLuaParams, currentLuaParam, currentNetParam, out extractValue))
{ {
int count = (nLuaParams - currentLuaParam) + 1; int count = (nLuaParams - currentLuaParam) + 1;
Type paramArrayType = currentNetParam.ParameterType.GetElementType(); Type paramArrayType = currentNetParam.ParameterType.GetElementType();
...@@ -1287,55 +1304,55 @@ namespace NLua ...@@ -1287,55 +1304,55 @@ namespace NLua
methodArg.Index = index; methodArg.Index = index;
methodArg.ExtractValue = extractValue; methodArg.ExtractValue = extractValue;
methodArg.IsParamsArray = true; methodArg.IsParamsArray = true;
methodArg.ParamsArrayType = paramArrayType; methodArg.ParameterType = paramArrayType;
argTypes.Add(methodArg); argTypes.Add(methodArg);
continue;
} }
else if (currentLuaParam > nLuaParams)
if (currentLuaParam > nLuaParams)
{ // Adds optional parameters { // Adds optional parameters
if (currentNetParam.IsOptional) if (!currentNetParam.IsOptional)
paramList.Add(currentNetParam.DefaultValue); return false;
else paramList.Add(currentNetParam.DefaultValue);
{ continue;
isMethod = false;
break;
}
} }
else if (IsTypeCorrect(luaState, currentLuaParam, currentNetParam, out extractValue))
{ // Type checking if (IsTypeCorrect(luaState, currentLuaParam, currentNetParam, out extractValue))
{ // Type checking
var value = extractValue(luaState, currentLuaParam); var value = extractValue(luaState, currentLuaParam);
paramList.Add(value); paramList.Add(value);
int index = paramList.Count - 1; int index = paramList.Count - 1;
var methodArg = new MethodArgs(); var methodArg = new MethodArgs();
methodArg.Index = index; methodArg.Index = index;
methodArg.ExtractValue = extractValue; methodArg.ExtractValue = extractValue;
methodArg.ParameterType = currentNetParam.ParameterType;
argTypes.Add(methodArg); argTypes.Add(methodArg);
if (currentNetParam.ParameterType.IsByRef) if (currentNetParam.ParameterType.IsByRef)
outList.Add(index); outList.Add(index);
currentLuaParam++; currentLuaParam++;
continue;
} }
else if (currentNetParam.IsOptional)
if (currentNetParam.IsOptional)
{
paramList.Add(currentNetParam.DefaultValue); paramList.Add(currentNetParam.DefaultValue);
else continue;
{ // No match
isMethod = false;
break;
} }
return false;
} }
if (currentLuaParam != nLuaParams + 1) // Number of parameters does not match if (currentLuaParam != nLuaParams + 1) // Number of parameters does not match
isMethod = false; return false;
methodCache.args = paramList.ToArray();
methodCache.cachedMethod = method;
methodCache.outList = outList.ToArray();
methodCache.argTypes = argTypes.ToArray();
if (isMethod) return true;
{
methodCache.args = paramList.ToArray();
methodCache.cachedMethod = method;
methodCache.outList = outList.ToArray();
methodCache.argTypes = argTypes.ToArray();
}
return isMethod;
} }
/// <summary> /// <summary>
...@@ -1348,7 +1365,7 @@ namespace NLua ...@@ -1348,7 +1365,7 @@ namespace NLua
/// <returns></returns> /// <returns></returns>
private bool IsTypeCorrect(LuaState luaState, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue) private bool IsTypeCorrect(LuaState luaState, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue)
{ {
extractValue = translator.typeChecker.CheckLuaType(luaState, currentLuaParam, currentNetParam.ParameterType); extractValue = _translator.typeChecker.CheckLuaType(luaState, currentLuaParam, currentNetParam.ParameterType);
return extractValue != null; return extractValue != null;
} }
...@@ -1365,7 +1382,7 @@ namespace NLua ...@@ -1365,7 +1382,7 @@ namespace NLua
if (luaType == LuaType.Table) if (luaType == LuaType.Table)
{ {
extractValue = translator.typeChecker.GetExtractor(typeof(LuaTable)); extractValue = _translator.typeChecker.GetExtractor(typeof(LuaTable));
if (extractValue != null) if (extractValue != null)
return true; return true;
} }
...@@ -1373,7 +1390,7 @@ namespace NLua ...@@ -1373,7 +1390,7 @@ namespace NLua
{ {
Type paramElementType = currentNetParam.ParameterType.GetElementType(); Type paramElementType = currentNetParam.ParameterType.GetElementType();
extractValue = translator.typeChecker.CheckLuaType(luaState, currentLuaParam, paramElementType); extractValue = _translator.typeChecker.CheckLuaType(luaState, currentLuaParam, paramElementType);
if (extractValue != null) if (extractValue != null)
return true; return true;
......
...@@ -34,7 +34,7 @@ namespace NLua.Method ...@@ -34,7 +34,7 @@ namespace NLua.Method
readonly string _methodName; readonly string _methodName;
readonly MethodInfo[] _members; readonly MethodInfo[] _members;
MethodCache _lastCalledMethod; private MethodCache _lastCalledMethod;
/* /*
...@@ -46,10 +46,12 @@ namespace NLua.Method ...@@ -46,10 +46,12 @@ namespace NLua.Method
_translator = translator; _translator = translator;
_target = target; _target = target;
_extractTarget = translator.typeChecker.GetExtractor(targetType); _extractTarget = translator.typeChecker.GetExtractor(targetType);
_lastCalledMethod = new MethodCache();
_method = method; _method = method;
_methodName = method.Name; _methodName = method.Name;
_isStatic = method.IsStatic; _isStatic = method.IsStatic;
} }
/* /*
...@@ -62,6 +64,7 @@ namespace NLua.Method ...@@ -62,6 +64,7 @@ namespace NLua.Method
_translator = translator; _translator = translator;
_methodName = methodName; _methodName = methodName;
_extractTarget = translator.typeChecker.GetExtractor(targetType); _extractTarget = translator.typeChecker.GetExtractor(targetType);
_lastCalledMethod = new MethodCache();
_isStatic = (bindingType & BindingFlags.Static) == BindingFlags.Static; _isStatic = (bindingType & BindingFlags.Static) == BindingFlags.Static;
_members = GetMethodsRecursively(targetType.UnderlyingSystemType, _members = GetMethodsRecursively(targetType.UnderlyingSystemType,
...@@ -90,6 +93,184 @@ namespace NLua.Method ...@@ -90,6 +93,184 @@ namespace NLua.Method
return _translator.interpreter.SetPendingException(e); return _translator.interpreter.SetPendingException(e);
} }
void FillMethodArguments(LuaState luaState, int numStackToSkip)
{
object[] args = _lastCalledMethod.args;
for (int i = 0; i < _lastCalledMethod.argTypes.Length; i++)
{
MethodArgs type = _lastCalledMethod.argTypes[i];
int index = i + 1 + numStackToSkip;
if (_lastCalledMethod.argTypes[i].IsParamsArray)
{
int count = _lastCalledMethod.argTypes.Length - i;
Array paramArray = _translator.TableToArray(luaState, type.ExtractValue, type.ParameterType, index, count);
args[_lastCalledMethod.argTypes[i].Index] = paramArray;
}
else
{
args[type.Index] = type.ExtractValue(luaState, index);
}
if (_lastCalledMethod.args[_lastCalledMethod.argTypes[i].Index] == null &&
!luaState.IsNil(i + 1 + numStackToSkip))
throw new LuaException(string.Format("Argument number {0} is invalid", (i + 1)));
}
}
int PushReturnValue(LuaState luaState)
{
int nReturnValues = 0;
// Pushes out and ref return values
for (int index = 0; index < _lastCalledMethod.outList.Length; index++)
{
nReturnValues++;
_translator.Push(luaState, _lastCalledMethod.args[_lastCalledMethod.outList[index]]);
}
// If not return void,we need add 1,
// or we will lost the function's return value
// when call dotnet function like "int foo(arg1,out arg2,out arg3)" in Lua code
if (!_lastCalledMethod.IsReturnVoid && nReturnValues > 0)
nReturnValues++;
return nReturnValues < 1 ? 1 : nReturnValues;
}
int CallInvoke(LuaState luaState, MethodBase method, object targetObject)
{
if (!luaState.CheckStack(_lastCalledMethod.outList.Length + 6))
throw new LuaException("Lua stack overflow");
try
{
if (method.IsConstructor)
_translator.Push(luaState, ((ConstructorInfo)method).Invoke(_lastCalledMethod.args));
else
_translator.Push(luaState, method.Invoke(targetObject, _lastCalledMethod.args));
}
catch (TargetInvocationException e)
{
// Failure of method invocation
if (_translator.interpreter.UseTraceback)
e.GetBaseException().Data["Traceback"] = _translator.interpreter.GetDebugTraceback();
return SetPendingException(e.GetBaseException());
}
catch (Exception e)
{
return SetPendingException(e);
}
return PushReturnValue(luaState);
}
bool IsMethodCached(LuaState luaState, int numArgsPassed, int skipParams)
{
if (_lastCalledMethod.cachedMethod == null)
return false;
if (numArgsPassed != _lastCalledMethod.argTypes.Length)
return false;
// If there is no method overloads, is ok to use the cached method
if (_members.Length == 1)
return true;
return _translator.MatchParameters(luaState, _lastCalledMethod.cachedMethod, _lastCalledMethod, skipParams);
}
int CallMethodFromName(LuaState luaState)
{
object targetObject = null;
if (!_isStatic)
targetObject = _extractTarget(luaState, 1);
int numStackToSkip =
_isStatic
? 0
: 1; // If this is an instance invoe we will have an extra arg on the stack for the targetObject
int numArgsPassed = luaState.GetTop() - numStackToSkip;
// Cached?
if (IsMethodCached(luaState, numArgsPassed, numStackToSkip))
{
MethodBase method = _lastCalledMethod.cachedMethod;
if (!luaState.CheckStack(_lastCalledMethod.outList.Length + 6))
throw new LuaException("Lua stack overflow");
FillMethodArguments(luaState, numStackToSkip);
return CallInvoke(luaState, method, targetObject);
}
// If we are running an instance variable, we can now pop the targetObject from the stack
if (!_isStatic)
{
if (targetObject == null)
{
_translator.ThrowError(luaState,
string.Format("instance method '{0}' requires a non null target object", _methodName));
luaState.PushNil();
return 1;
}
luaState.Remove(1); // Pops the receiver
}
bool hasMatch = false;
string candidateName = null;
foreach (var member in _members)
{
if (member.ReflectedType == null)
continue;
candidateName = member.ReflectedType.Name + "." + member.Name;
bool isMethod = _translator.MatchParameters(luaState, member, _lastCalledMethod, 0);
if (isMethod)
{
hasMatch = true;
break;
}
}
if (!hasMatch)
{
string msg = (candidateName == null)
? "Invalid arguments to method call"
: ("Invalid arguments to method: " + candidateName);
_translator.ThrowError(luaState, msg);
luaState.PushNil();
return 1;
}
return CallInvoke(luaState, _lastCalledMethod.cachedMethod, targetObject);
}
int CallInvokeOnGenericMethod(LuaState luaState, MethodInfo methodToCall, object targetObject)
{
_translator.MatchParameters(luaState, methodToCall, _lastCalledMethod, 0);
//need to make a concrete type of the generic method definition
var typeArgs = new List<Type>();
foreach (object arg in _lastCalledMethod.args)
typeArgs.Add(arg.GetType());
var concreteMethod = methodToCall.MakeGenericMethod(typeArgs.ToArray());
_translator.Push(luaState, concreteMethod.Invoke(targetObject, _lastCalledMethod.args));
return PushReturnValue(luaState);
}
/* /*
* Calls the method. Receives the arguments from the Lua stack * Calls the method. Receives the arguments from the Lua stack
* and returns values in it. * and returns values in it.
...@@ -100,214 +281,49 @@ namespace NLua.Method ...@@ -100,214 +281,49 @@ namespace NLua.Method
MethodBase methodToCall = _method; MethodBase methodToCall = _method;
object targetObject = _target; object targetObject = _target;
bool failedCall = true;
int nReturnValues = 0;
if (!luaState.CheckStack(5)) if (!luaState.CheckStack(5))
throw new LuaException("Lua stack overflow"); throw new LuaException("Lua stack overflow");
bool isStatic = _isStatic;
SetPendingException(null); SetPendingException(null);
// Method from name
if (methodToCall == null) if (methodToCall == null)
{ // Method from name return CallMethodFromName(luaState);
if (isStatic)
targetObject = null; // Method from MethodBase instance
else if (!methodToCall.ContainsGenericParameters)
{
if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null)
{
targetObject = _extractTarget(luaState, 1); targetObject = _extractTarget(luaState, 1);
luaState.Remove(1); // Pops the receiver
if (_lastCalledMethod.cachedMethod != null)
{ // Cached?
int numStackToSkip = isStatic ? 0 : 1; // If this is an instance invoe we will have an extra arg on the stack for the targetObject
int numArgsPassed = luaState.GetTop() - numStackToSkip;
MethodBase method = _lastCalledMethod.cachedMethod;
if (numArgsPassed == _lastCalledMethod.argTypes.Length)
{ // No. of args match?
if (!luaState.CheckStack(_lastCalledMethod.outList.Length + 6))
throw new LuaException("Lua stack overflow");
object[] args = _lastCalledMethod.args;
try
{
for (int i = 0; i < _lastCalledMethod.argTypes.Length; i++)
{
MethodArgs type = _lastCalledMethod.argTypes[i];
int index = i + 1 + numStackToSkip;
if (_lastCalledMethod.argTypes[i].IsParamsArray)
{
int count = _lastCalledMethod.argTypes.Length - i;
Array paramArray = _translator.TableToArray(luaState, type.ExtractValue, type.ParamsArrayType, index, count);
args[_lastCalledMethod.argTypes[i].Index] = paramArray;
}
else
{
args[type.Index] = type.ExtractValue(luaState, index);
}
if (_lastCalledMethod.args[_lastCalledMethod.argTypes[i].Index] == null &&
!luaState.IsNil(i + 1 + numStackToSkip))
throw new LuaException(string.Format("Argument number {0} is invalid", (i + 1)));
}
if (_isStatic)
_translator.Push(luaState, method.Invoke(null, _lastCalledMethod.args));
else
{
if (method.IsConstructor)
_translator.Push(luaState, ((ConstructorInfo)method).Invoke(_lastCalledMethod.args));
else
_translator.Push(luaState, method.Invoke(targetObject, _lastCalledMethod.args));
}
failedCall = false;
}
catch (TargetInvocationException e)
{
// Failure of method invocation
if (_translator.interpreter.UseTraceback) e.GetBaseException().Data["Traceback"] = _translator.interpreter.GetDebugTraceback();
return SetPendingException(e.GetBaseException());
}
catch (Exception e)
{
if (_members.Length == 1) // Is the method overloaded?
// No, throw error
return SetPendingException(e);
}
}
} }
// Cache miss if (!_translator.MatchParameters(luaState, methodToCall, _lastCalledMethod, 0))
if (failedCall)
{ {
// System.Diagnostics.Debug.WriteLine("cache miss on " + methodName); _translator.ThrowError(luaState, "Invalid arguments to method call");
// If we are running an instance variable, we can now pop the targetObject from the stack luaState.PushNil();
if (!isStatic) return 1;
{
if (targetObject == null)
{
_translator.ThrowError(luaState, string.Format("instance method '{0}' requires a non null target object", _methodName));
luaState.PushNil();
return 1;
}
luaState.Remove(1); // Pops the receiver
}
bool hasMatch = false;
string candidateName = null;
foreach (var member in _members)
{
candidateName = member.ReflectedType.Name + "." + member.Name;
bool isMethod = _translator.MatchParameters(luaState, member, ref _lastCalledMethod);
if (isMethod)
{
hasMatch = true;
break;
}
}
if (!hasMatch)
{
string msg = (candidateName == null) ? "Invalid arguments to method call" : ("Invalid arguments to method: " + candidateName);
_translator.ThrowError(luaState, msg);
luaState.PushNil();
return 1;
}
} }
} }
else else
{ // Method from MethodBase instance
if (methodToCall.ContainsGenericParameters)
{
_translator.MatchParameters(luaState, methodToCall, ref _lastCalledMethod);
if (methodToCall.IsGenericMethodDefinition)
{
//need to make a concrete type of the generic method definition
var typeArgs = new List<Type>();
foreach (object arg in _lastCalledMethod.args)
typeArgs.Add(arg.GetType());
var concreteMethod = ((MethodInfo)methodToCall).MakeGenericMethod(typeArgs.ToArray());
_translator.Push(luaState, concreteMethod.Invoke(targetObject, _lastCalledMethod.args));
failedCall = false;
}
else if (methodToCall.ContainsGenericParameters)
{
_translator.ThrowError(luaState, "Unable to invoke method on generic class as the current method is an open generic method");
luaState.PushNil();
return 1;
}
}
else
{
if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null)
{
targetObject = _extractTarget(luaState, 1);
luaState.Remove(1); // Pops the receiver
}
if (!_translator.MatchParameters(luaState, methodToCall, ref _lastCalledMethod))
{
_translator.ThrowError(luaState, "Invalid arguments to method call");
luaState.PushNil();
return 1;
}
}
}
if (failedCall)
{ {
if (!luaState.CheckStack(_lastCalledMethod.outList.Length + 6)) if (!methodToCall.IsGenericMethodDefinition)
throw new LuaException("Lua stack overflow");
try
{
if (isStatic)
_translator.Push(luaState, _lastCalledMethod.cachedMethod.Invoke(null, _lastCalledMethod.args));
else
{
if (_lastCalledMethod.cachedMethod.IsConstructor)
_translator.Push(luaState, ((ConstructorInfo)_lastCalledMethod.cachedMethod).Invoke(_lastCalledMethod.args));
else
_translator.Push(luaState, _lastCalledMethod.cachedMethod.Invoke(targetObject, _lastCalledMethod.args));
}
}
catch (TargetInvocationException e)
{ {
if (_translator.interpreter.UseTraceback) e.GetBaseException().Data["Traceback"] = _translator.interpreter.GetDebugTraceback(); _translator.ThrowError(luaState,
return SetPendingException(e.GetBaseException()); "Unable to invoke method on generic class as the current method is an open generic method");
luaState.PushNil();
return 1;
} }
catch (Exception e)
{
return SetPendingException(e);
}
}
// Pushes out and ref return values return CallInvokeOnGenericMethod(luaState, (MethodInfo) methodToCall, targetObject);
for (int index = 0; index < _lastCalledMethod.outList.Length; index++)
{
nReturnValues++;
_translator.Push(luaState, _lastCalledMethod.args[_lastCalledMethod.outList[index]]);
} }
if (_isStatic)
targetObject = null;
// If not return void,we need add 1, return CallInvoke(luaState, _lastCalledMethod.cachedMethod, targetObject);
// or we will lost the function's return value
// when call dotnet function like "int foo(arg1,out arg2,out arg3)" in Lua code
if (!_lastCalledMethod.IsReturnVoid && nReturnValues > 0)
nReturnValues++;
return nReturnValues < 1 ? 1 : nReturnValues;
} }
} }
} }
\ No newline at end of file
...@@ -5,13 +5,14 @@ namespace NLua.Method ...@@ -5,13 +5,14 @@ namespace NLua.Method
/* /*
* Parameter information * Parameter information
*/ */
struct MethodArgs class MethodArgs
{ {
// Position of parameter // Position of parameter
public int Index; public int Index;
public Type ParameterType;
// Type-conversion function // Type-conversion function
public ExtractValue ExtractValue; public ExtractValue ExtractValue;
public bool IsParamsArray; public bool IsParamsArray;
public Type ParamsArrayType;
} }
} }
\ No newline at end of file
...@@ -4,8 +4,14 @@ using NLua.Extensions; ...@@ -4,8 +4,14 @@ using NLua.Extensions;
namespace NLua.Method namespace NLua.Method
{ {
struct MethodCache class MethodCache
{ {
public MethodCache()
{
args = new object[0];
argTypes = new MethodArgs[0];
outList = new int[0];
}
private MethodBase _cachedMethod; private MethodBase _cachedMethod;
public MethodBase cachedMethod { public MethodBase cachedMethod {
......
...@@ -331,9 +331,10 @@ namespace NLua ...@@ -331,9 +331,10 @@ namespace NLua
return null; return null;
} }
public bool IsExtensionMethodPresent(Type type, string name) public bool TryGetExtensionMethod(Type type, string name, out MethodInfo method)
{ {
return GetExtensionMethod(type, name) != null; method = GetExtensionMethod(type, name);
return method != null;
} }
public MethodInfo GetExtensionMethod(Type type, string name) public MethodInfo GetExtensionMethod(Type type, string name)
...@@ -1009,9 +1010,9 @@ namespace NLua ...@@ -1009,9 +1010,9 @@ namespace NLua
* Checks if the method matches the arguments in the Lua stack, getting * Checks if the method matches the arguments in the Lua stack, getting
* the arguments if it does. * the arguments if it does.
*/ */
internal bool MatchParameters(LuaState luaState, MethodBase method, ref MethodCache methodCache) internal bool MatchParameters(LuaState luaState, MethodBase method, MethodCache methodCache, int skipParam)
{ {
return metaFunctions.MatchParameters(luaState, method, ref methodCache); return metaFunctions.MatchParameters(luaState, method, methodCache, skipParam);
} }
internal Array TableToArray(LuaState luaState, ExtractValue extractValue, Type paramArrayType, int startIndex, int count) internal Array TableToArray(LuaState luaState, ExtractValue extractValue, Type paramArrayType, int startIndex, int count)
......
...@@ -188,8 +188,11 @@ namespace NLuaTest ...@@ -188,8 +188,11 @@ namespace NLuaTest
lua.DoString("test=TestClass()"); lua.DoString("test=TestClass()");
lua.DoString("test:MethodOverload()"); lua.DoString("test:MethodOverload()");
lua.DoString("test:MethodOverload(test)"); lua.DoString("test:MethodOverload(test)");
lua.DoString("test:MethodOverload(test)");
lua.DoString("test:MethodOverload(1,1,1)"); lua.DoString("test:MethodOverload(1,1,1)");
lua.DoString("test:MethodOverload(2,2,i)\r\nprint(i)"); lua.DoString("i = test:MethodOverload(2,2)\r\nprint(i)");
int i = (int) lua.GetNumber("i");
Assert.AreEqual(5, i, "#1");
} }
} }
...@@ -1677,7 +1680,7 @@ namespace NLuaTest ...@@ -1677,7 +1680,7 @@ namespace NLuaTest
lua.DoString("test=TestClass()"); lua.DoString("test=TestClass()");
lua.DoString("setMethod=luanet.get_method_bysig(TestClass,'setVal','System.String')"); lua.DoString("setMethod=luanet.get_method_bysig(TestClass,'setVal','System.String')");
lua.DoString("setMethod(test,'test')"); lua.DoString("setMethod(test,'test')");
TestTypes.TestClass test = (TestTypes.TestClass)lua["test"]; var test = (TestTypes.TestClass)lua["test"];
Assert.AreEqual("test", test.getStrVal()); Assert.AreEqual("test", test.getStrVal());
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment