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
{
if (!luaState.IsString(stackPos))
return null;
string retVal = luaState.ToString(stackPos, false);
return retVal;
return luaState.ToString(stackPos, false);
}
private object GetAsTable(LuaState luaState, int stackPos)
......@@ -354,10 +353,12 @@ namespace NLua
{
object obj = _translator.GetNetObject(luaState, stackPos);
if (obj == null && luaState.Type(stackPos) == LuaType.Table)
{
if (luaState.GetMetaField(stackPos, "__index") != LuaType.Nil)
{
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))
{
luaState.Insert(stackPos);
......@@ -366,8 +367,6 @@ namespace NLua
}
else
luaState.SetTop(-2);
}
}
return obj;
}
......
......@@ -30,6 +30,7 @@ namespace NLua.Extensions
luaState.RawGetInteger(LuaRegistry.Index, reference);
}
// ReSharper disable once IdentifierTypo
public static void Unref (this LuaState luaState, int reference)
{
luaState.Unref(LuaRegistry.Index, reference);
......
......@@ -47,25 +47,21 @@ namespace NLua
/// <summary>
/// True while a script is being executed
/// </summary>
public bool IsExecuting => executing;
public bool IsExecuting => _executing;
public LuaState State => _luaState;
private ObjectTranslator _translator;
/// <summary>
/// Used to protect the (global) object translator pool during add/remove
/// </summary>
private static readonly object translatorPoolLock = new object();
/// <summary>
/// Used to ensure multiple .net threads all get serialized by this single lock for access to the lua stack/objects
/// </summary>
//private object luaLock = new object();
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.
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 rawget = rawget
// local import_type = luanet.import_type
......@@ -106,7 +102,7 @@ namespace NLua
// -- Preload the mscorlib assembly
// 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.
//--- Makes for super easy use of LuaInterface glue
......@@ -282,12 +278,11 @@ namespace NLua
_luaState.SetTable(-3);
_luaState.PopGlobalTable();
_translator = new ObjectTranslator(this, _luaState);
lock (translatorPoolLock)
{
ObjectTranslatorPool.Instance.Add(_luaState, _translator);
}
_luaState.PopGlobalTable();
_luaState.DoString(initLuanet);
_luaState.DoString(InitLuanet);
}
public void Close()
......@@ -295,13 +290,10 @@ namespace NLua
if (_StatePassed || _luaState == null)
return;
lock (translatorPoolLock)
{
_luaState.Close();
ObjectTranslatorPool.Instance.Remove(_luaState);
_luaState = null;
}
}
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
......@@ -388,7 +380,7 @@ namespace NLua
public LuaFunction LoadString(string chunk, string name)
{
int oldTop = _luaState.GetTop();
executing = true;
_executing = true;
try
{
......@@ -397,7 +389,7 @@ namespace NLua
}
finally
{
executing = false;
_executing = false;
}
var result = _translator.GetFunction(_luaState, -1);
......@@ -414,7 +406,7 @@ namespace NLua
public LuaFunction LoadString(byte[] chunk, string name)
{
int oldTop = _luaState.GetTop();
executing = true;
_executing = true;
try
{
......@@ -423,7 +415,7 @@ namespace NLua
}
finally
{
executing = false;
_executing = false;
}
var result = _translator.GetFunction(_luaState, -1);
......@@ -457,7 +449,7 @@ namespace NLua
public object[] DoString(byte[] chunk, string chunkName = "chunk")
{
int oldTop = _luaState.GetTop();
executing = true;
_executing = true;
if (_luaState.LoadBuffer(chunk, chunkName) != LuaStatus.OK)
ThrowExceptionFromError(oldTop);
......@@ -479,7 +471,7 @@ namespace NLua
}
finally
{
executing = false;
_executing = false;
}
}
......@@ -492,7 +484,7 @@ namespace NLua
public object[] DoString(string chunk, string chunkName = "chunk")
{
int oldTop = _luaState.GetTop();
executing = true;
_executing = true;
if (_luaState.LoadString(chunk, chunkName) != LuaStatus.OK)
ThrowExceptionFromError(oldTop);
......@@ -514,7 +506,7 @@ namespace NLua
}
finally
{
executing = false;
_executing = false;
}
}
......@@ -529,7 +521,7 @@ namespace NLua
if (_luaState.LoadFile(fileName) != LuaStatus.OK)
ThrowExceptionFromError(oldTop);
executing = true;
_executing = true;
int errorFunctionIndex = 0;
if (UseTraceback)
......@@ -547,7 +539,7 @@ namespace NLua
}
finally
{
executing = false;
_executing = false;
}
}
......@@ -776,9 +768,10 @@ namespace NLua
CodeGeneration.Instance.RegisterLuaClassType(klass, luaClass);
}
// ReSharper disable once InconsistentNaming
public void LoadCLRPackage()
{
_luaState.DoString(clr_package);
_luaState.DoString(ClrPackage);
}
/*
* Gets a function global variable as a delegate of
......@@ -821,7 +814,7 @@ namespace NLua
_translator.Push(_luaState, args[i]);
}
executing = true;
_executing = true;
try
{
......@@ -838,7 +831,7 @@ namespace NLua
}
finally
{
executing = false;
_executing = false;
}
if (returnTypes != null)
......@@ -934,7 +927,7 @@ namespace NLua
{
if (_hookCallback == null)
{
_hookCallback = new LuaHookFunction(Lua.DebugHookCallback);
_hookCallback = DebugHookCallback;
_luaState.SetHook(_hookCallback, mask, count);
}
......@@ -1164,7 +1157,7 @@ namespace NLua
_luaState.SetTop(oldTop);
}
public LuaFunction RegisterFunction(string path, MethodBase function /*MethodInfo function*/)
public LuaFunction RegisterFunction(string path, MethodBase function)
{
return RegisterFunction(path, null, function);
}
......@@ -1173,7 +1166,7 @@ namespace NLua
* Registers an object's method as a Lua function (global or table field)
* 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
int oldTop = _luaState.GetTop();
......@@ -1198,6 +1191,7 @@ namespace NLua
return equal;
}
// ReSharper disable once InconsistentNaming
internal void PushCSFunction(LuaNativeFunction function)
{
_translator.PushFunction(_luaState, function);
......
......@@ -43,33 +43,32 @@ namespace NLua
public LuaNativeFunction LessThanFunction { get; }
public LuaNativeFunction LessThanOrEqualFunction { get; }
readonly Dictionary<object, Dictionary<object, object>> memberCache = new Dictionary<object, Dictionary<object, object>>();
readonly ObjectTranslator translator;
readonly Dictionary<object, Dictionary<object, object>> _memberCache = new Dictionary<object, Dictionary<object, object>>();
readonly ObjectTranslator _translator;
/*
* __index metafunction for CLR objects. Implemented in Lua.
*/
static readonly string luaIndexFunction =
@"local function index(obj,name)
local meta = getmetatable(obj)
local cached = meta.cache[name]
if cached ~= nil then
return cached
else
local value,isFunc = get_object_member(obj,name)
if isFunc then
meta.cache[name]=value
end
return value
end
end
return index";
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 meta = getmetatable(obj)
// local cached = meta.cache[name]
// if cached ~= nil then
// return cached
// else
// local value,isFunc = get_object_member(obj,name)
// if isFunc then
// meta.cache[name]=value
// end
// return value
// end
//end
//return index";
public static string LuaIndexFunction => luaIndexFunction;
public MetaFunctions(ObjectTranslator translator)
{
this.translator = translator;
_translator = translator;
GcFunction = CollectObject;
ToStringFunction = ToStringLua;
IndexFunction = GetMethod;
......@@ -345,16 +344,16 @@ namespace NLua
private int GetMethodInternal(LuaState luaState)
{
object obj = translator.GetRawNetObject(luaState, 1);
object obj = _translator.GetRawNetObject(luaState, 1);
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();
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
var objType = obj.GetType();
var proxyType = new ProxyType(objType);
......@@ -362,49 +361,61 @@ namespace NLua
// 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,
// 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);
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)
if (objType.IsArray && index is double)
{
int intIndex = (int)((double)index);
int intIndex = (int)(double)index;
Type type = objType.UnderlyingSystemType;
if (type == typeof(float[]))
{
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;
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;
translator.Push(luaState, arr[intIndex]);
}
else
{
object[] arr = (object[])obj;
translator.Push(luaState, arr[intIndex]);
_translator.Push(luaState, arr[intIndex]);
return 0;
}
object[] arrObj = (object[])obj;
_translator.Push(luaState, arrObj[intIndex]);
return 0;
}
else
{
if (!string.IsNullOrEmpty(methodName) && IsExtensionMethodPresent(objType, methodName))
object method;
if (!string.IsNullOrEmpty(methodName) && TryGetExtensionMethod(objType, methodName, out method))
{
return GetExtensionMethod(luaState, objType, obj, methodName);
return PushExtensionMethod(luaState, objType, obj, methodName, method);
}
// Try to use get_Item to index into this .net object
var methods = objType.GetMethods();
......@@ -422,13 +433,13 @@ namespace NLua
if (actualParams.Length != 1)
{
translator.ThrowError(luaState, "method not found (or no indexer): " + index);
_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);
index = _translator.GetAsType(luaState, 2, actualParams[0].ParameterType);
object[] args = new object[1];
// Just call the indexer - if out of bounds an exception will happen
......@@ -437,23 +448,22 @@ namespace NLua
try
{
object result = methodInfo.Invoke(obj, args);
translator.Push(luaState, result);
_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 ");
_translator.ThrowError(luaState, "key '" + index + "' not found ");
else
translator.ThrowError(luaState, "exception indexing '" + index + "' " + e.Message);
_translator.ThrowError(luaState, "exception indexing '" + index + "' " + e.Message);
luaState.PushNil();
}
}
}
}
luaState.PushBoolean(false);
return 2;
return 0;
}
/*
......@@ -473,11 +483,11 @@ namespace NLua
private int GetBaseMethodInternal(LuaState luaState)
{
object obj = translator.GetRawNetObject(luaState, 1);
object obj = _translator.GetRawNetObject(luaState, 1);
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.PushBoolean(false);
return 2;
......@@ -522,35 +532,41 @@ namespace NLua
return members.Length > 0;
}
bool IsExtensionMethodPresent(Type type, string name)
bool TryGetExtensionMethod(Type type, string name, out object method)
{
object cachedMember = CheckMemberCache(type, name);
if (cachedMember != null)
{
method = cachedMember;
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)
{
translator.PushFunction(luaState, cachedMember);
translator.Push(luaState, true);
_translator.PushFunction(luaState, cachedMember);
_translator.Push(luaState, true);
return 2;
}
MethodInfo methodInfo = translator.GetExtensionMethod(type, name);
var methodWrapper = new LuaMethodWrapper(translator, obj, new ProxyType(type), methodInfo);
var methodInfo = (MethodInfo)method;
var methodWrapper = new LuaMethodWrapper(_translator, obj, new ProxyType(type), methodInfo);
var invokeDelegate = new LuaNativeFunction(methodWrapper.InvokeFunction);
SetMemberCache(type, name, invokeDelegate);
translator.PushFunction(luaState, invokeDelegate);
translator.Push(luaState, true);
_translator.PushFunction(luaState, invokeDelegate);
_translator.Push(luaState, true);
return 2;
}
......@@ -568,8 +584,8 @@ namespace NLua
if (cachedMember is LuaNativeFunction)
{
translator.PushFunction(luaState, (LuaNativeFunction)cachedMember);
translator.Push(luaState, true);
_translator.PushFunction(luaState, (LuaNativeFunction)cachedMember);
_translator.Push(luaState, true);
return 2;
}
if (cachedMember != null)
......@@ -605,7 +621,7 @@ namespace NLua
try
{
var value = field.GetValue(obj);
translator.Push(luaState, value);
_translator.Push(luaState, value);
}
catch
{
......@@ -622,7 +638,7 @@ namespace NLua
try
{
object value = property.GetValue(obj, null);
translator.Push(luaState, value);
_translator.Push(luaState, value);
}
catch (ArgumentException)
{
......@@ -644,42 +660,42 @@ namespace NLua
if (cachedMember == null)
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)
{
if (member.MemberType == MemberTypes.NestedType)
if (member.MemberType == MemberTypes.NestedType && member.DeclaringType != null)
{
if (cachedMember == null)
SetMemberCache(objType, methodName, member);
// Find the name of our class
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
string longname = dectype.FullName + "+" + name;
var nestedType = translator.FindType(longname);
translator.PushType(luaState, nestedType);
string longName = decType.FullName + "+" + name;
var nestedType = _translator.FindType(longName);
_translator.PushType(luaState, nestedType);
}
else
{
// 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;
if (cachedMember == null)
SetMemberCache(objType, methodName, wrapper);
translator.PushFunction(luaState, wrapper);
translator.Push(luaState, true);
_translator.PushFunction(luaState, wrapper);
_translator.Push(luaState, true);
return 2;
}
}
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
translator.ThrowError(luaState, "Can't pass instance to static method " + methodName);
_translator.ThrowError(luaState, "Can't pass instance to static method " + methodName);
luaState.PushNil();
}
}
......@@ -691,12 +707,12 @@ namespace NLua
// 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
// 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();
}
// Push false because we are NOT returning a function (see luaIndexFunction)
translator.Push(luaState, false);
_translator.Push(luaState, false);
return 2;
}
......@@ -712,7 +728,7 @@ namespace NLua
{
Dictionary<object, object> members;
if (!memberCache.TryGetValue(objType, out members))
if (!_memberCache.TryGetValue(objType, out members))
return null;
object memberValue;
......@@ -736,14 +752,14 @@ namespace NLua
Dictionary<object, object> members;
Dictionary<object, object> memberCacheValue;
if (memberCache.TryGetValue(objType, out memberCacheValue))
if (_memberCache.TryGetValue(objType, out memberCacheValue))
{
members = memberCacheValue;
}
else
{
members = new Dictionary<object, object>();
memberCache[objType] = members;
_memberCache[objType] = members;
}
members[memberName] = member;
......@@ -767,11 +783,11 @@ namespace NLua
private int SetFieldOrPropertyInternal(LuaState luaState)
{
object target = translator.GetRawNetObject(luaState, 1);
object target = _translator.GetRawNetObject(luaState, 1);
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;
}
......@@ -791,7 +807,7 @@ namespace NLua
{
int index = (int)luaState.ToNumber(2);
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);
}
else
......@@ -804,9 +820,9 @@ namespace NLua
var valueType = args[1].ParameterType;
// 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;
object index = translator.GetAsType(luaState, 2, indexType);
object index = _translator.GetAsType(luaState, 2, indexType);
object[] methodArgs = new object[2];
......@@ -816,7 +832,7 @@ namespace NLua
setter.Invoke(target, methodArgs);
}
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)
......@@ -881,7 +897,7 @@ namespace NLua
if (member.MemberType == MemberTypes.Field)
{
var field = (FieldInfo)member;
object val = translator.GetAsType(luaState, 3, field.FieldType);
object val = _translator.GetAsType(luaState, 3, field.FieldType);
try
{
......@@ -897,7 +913,7 @@ namespace NLua
if (member.MemberType == MemberTypes.Property)
{
var property = (PropertyInfo)member;
object val = translator.GetAsType(luaState, 3, property.PropertyType);
object val = _translator.GetAsType(luaState, 3, property.PropertyType);
try
{
......@@ -925,7 +941,7 @@ namespace NLua
bool success = TrySetMember(luaState, targetType, target, bindingType, out detail);
if (!success)
translator.ThrowError(luaState, detail);
_translator.ThrowError(luaState, detail);
return 0;
}
......@@ -934,6 +950,7 @@ namespace NLua
/// Convert a C# exception into a Lua error
/// </summary>
/// <param name="e"></param>
/// <param name="luaState"></param>
/// We try to look into the exception to give the most meaningful description
void ThrowError(LuaState luaState, Exception e)
{
......@@ -943,7 +960,7 @@ namespace NLua
if (te != null)
e = te.InnerException;
translator.ThrowError(luaState, e);
_translator.ThrowError(luaState, e);
}
/*
......@@ -962,11 +979,11 @@ namespace NLua
private int GetClassMethodInternal(LuaState luaState)
{
var klass = translator.GetRawNetObject(luaState, 1) as ProxyType;
var klass = _translator.GetRawNetObject(luaState, 1) as ProxyType;
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();
return 1;
}
......@@ -974,7 +991,7 @@ namespace NLua
if (luaState.IsNumber(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;
}
......@@ -1004,11 +1021,11 @@ namespace NLua
private int SetClassFieldOrPropertyInternal(LuaState luaState)
{
var target = translator.GetRawNetObject(luaState, 1) as ProxyType;
var target = _translator.GetRawNetObject(luaState, 1) as ProxyType;
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;
}
......@@ -1031,11 +1048,11 @@ namespace NLua
int CallDelegateInternal(LuaState luaState)
{
var del = translator.GetRawNetObject(luaState, 1) as Delegate;
var del = _translator.GetRawNetObject(luaState, 1) as Delegate;
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();
return 1;
}
......@@ -1044,7 +1061,7 @@ namespace NLua
var validDelegate = new MethodCache();
MethodBase methodDelegate = del.Method;
bool isOk = MatchParameters(luaState, methodDelegate, ref validDelegate);
bool isOk = MatchParameters(luaState, methodDelegate, validDelegate, 0);
if (isOk)
{
......@@ -1055,11 +1072,11 @@ namespace NLua
else
result = methodDelegate.Invoke(del.Target, validDelegate.args);
translator.Push(luaState, result);
_translator.Push(luaState, result);
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();
return 1;
}
......@@ -1083,11 +1100,11 @@ namespace NLua
private int CallConstructorInternal(LuaState luaState)
{
var klass = translator.GetRawNetObject(luaState, 1) as ProxyType;
var klass = _translator.GetRawNetObject(luaState, 1) as ProxyType;
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();
return 1;
}
......@@ -1099,14 +1116,14 @@ namespace NLua
foreach (var constructor in constructors)
{
bool isConstructor = MatchParameters(luaState, constructor, ref validConstructor);
bool isConstructor = MatchParameters(luaState, constructor, validConstructor, 0);
if (!isConstructor)
continue;
try
{
translator.Push(luaState, constructor.Invoke(validConstructor.args));
_translator.Push(luaState, constructor.Invoke(validConstructor.args));
}
catch (TargetInvocationException e)
{
......@@ -1125,13 +1142,13 @@ namespace NLua
int numLuaParams = luaState.GetTop();
if (numLuaParams == 0)
{
translator.Push(luaState, Activator.CreateInstance(klass.UnderlyingSystemType));
_translator.Push(luaState, Activator.CreateInstance(klass.UnderlyingSystemType));
return 1;
}
}
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));
luaState.PushNil();
return 1;
......@@ -1180,7 +1197,7 @@ namespace NLua
foreach (var op in operators)
{
bool isOk = translator.MatchParameters(luaState, op, ref validOperator);
bool isOk = translator.MatchParameters(luaState, op, validOperator, 0);
if (!isOk)
continue;
......@@ -1256,13 +1273,11 @@ namespace NLua
* if the match was successful. It it was also returns the information
* 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();
int currentLuaParam = 1;
int nLuaParams = luaState.GetTop();
int nLuaParams = luaState.GetTop() - skipParam;
var paramList = new List<object>();
var outList = new List<int>();
var argTypes = new List<MethodArgs>();
......@@ -1272,10 +1287,12 @@ namespace NLua
if (!currentNetParam.IsIn && currentNetParam.IsOut) // Skips out params
{
paramList.Add(null);
outList.Add(paramList.LastIndexOf(null));
outList.Add(paramList.Count - 1);
continue;
} // 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;
Type paramArrayType = currentNetParam.ParameterType.GetElementType();
......@@ -1287,21 +1304,20 @@ namespace NLua
methodArg.Index = index;
methodArg.ExtractValue = extractValue;
methodArg.IsParamsArray = true;
methodArg.ParamsArrayType = paramArrayType;
methodArg.ParameterType = paramArrayType;
argTypes.Add(methodArg);
continue;
}
else if (currentLuaParam > nLuaParams)
if (currentLuaParam > nLuaParams)
{ // Adds optional parameters
if (currentNetParam.IsOptional)
if (!currentNetParam.IsOptional)
return false;
paramList.Add(currentNetParam.DefaultValue);
else
{
isMethod = false;
break;
}
continue;
}
else if (IsTypeCorrect(luaState, currentLuaParam, currentNetParam, out extractValue))
if (IsTypeCorrect(luaState, currentLuaParam, currentNetParam, out extractValue))
{ // Type checking
var value = extractValue(luaState, currentLuaParam);
paramList.Add(value);
......@@ -1309,33 +1325,34 @@ namespace NLua
var methodArg = new MethodArgs();
methodArg.Index = index;
methodArg.ExtractValue = extractValue;
methodArg.ParameterType = currentNetParam.ParameterType;
argTypes.Add(methodArg);
if (currentNetParam.ParameterType.IsByRef)
outList.Add(index);
currentLuaParam++;
continue;
}
else if (currentNetParam.IsOptional)
if (currentNetParam.IsOptional)
{
paramList.Add(currentNetParam.DefaultValue);
else
{ // No match
isMethod = false;
break;
continue;
}
return false;
}
if (currentLuaParam != nLuaParams + 1) // Number of parameters does not match
isMethod = false;
return false;
if (isMethod)
{
methodCache.args = paramList.ToArray();
methodCache.cachedMethod = method;
methodCache.outList = outList.ToArray();
methodCache.argTypes = argTypes.ToArray();
}
return isMethod;
return true;
}
/// <summary>
......@@ -1348,7 +1365,7 @@ namespace NLua
/// <returns></returns>
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;
}
......@@ -1365,7 +1382,7 @@ namespace NLua
if (luaType == LuaType.Table)
{
extractValue = translator.typeChecker.GetExtractor(typeof(LuaTable));
extractValue = _translator.typeChecker.GetExtractor(typeof(LuaTable));
if (extractValue != null)
return true;
}
......@@ -1373,7 +1390,7 @@ namespace NLua
{
Type paramElementType = currentNetParam.ParameterType.GetElementType();
extractValue = translator.typeChecker.CheckLuaType(luaState, currentLuaParam, paramElementType);
extractValue = _translator.typeChecker.CheckLuaType(luaState, currentLuaParam, paramElementType);
if (extractValue != null)
return true;
......
......@@ -34,7 +34,7 @@ namespace NLua.Method
readonly string _methodName;
readonly MethodInfo[] _members;
MethodCache _lastCalledMethod;
private MethodCache _lastCalledMethod;
/*
......@@ -46,10 +46,12 @@ namespace NLua.Method
_translator = translator;
_target = target;
_extractTarget = translator.typeChecker.GetExtractor(targetType);
_lastCalledMethod = new MethodCache();
_method = method;
_methodName = method.Name;
_isStatic = method.IsStatic;
}
/*
......@@ -62,6 +64,7 @@ namespace NLua.Method
_translator = translator;
_methodName = methodName;
_extractTarget = translator.typeChecker.GetExtractor(targetType);
_lastCalledMethod = new MethodCache();
_isStatic = (bindingType & BindingFlags.Static) == BindingFlags.Static;
_members = GetMethodsRecursively(targetType.UnderlyingSystemType,
......@@ -90,48 +93,11 @@ namespace NLua.Method
return _translator.interpreter.SetPendingException(e);
}
/*
* Calls the method. Receives the arguments from the Lua stack
* and returns values in it.
*/
int Call(IntPtr state)
void FillMethodArguments(LuaState luaState, int numStackToSkip)
{
var luaState = LuaState.FromIntPtr(state);
MethodBase methodToCall = _method;
object targetObject = _target;
bool failedCall = true;
int nReturnValues = 0;
if (!luaState.CheckStack(5))
throw new LuaException("Lua stack overflow");
bool isStatic = _isStatic;
SetPendingException(null);
if (methodToCall == null)
{ // Method from name
if (isStatic)
targetObject = null;
else
targetObject = _extractTarget(luaState, 1);
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];
......@@ -142,7 +108,7 @@ namespace NLua.Method
if (_lastCalledMethod.argTypes[i].IsParamsArray)
{
int count = _lastCalledMethod.argTypes.Length - i;
Array paramArray = _translator.TableToArray(luaState, type.ExtractValue, type.ParamsArrayType, index, count);
Array paramArray = _translator.TableToArray(luaState, type.ExtractValue, type.ParameterType, index, count);
args[_lastCalledMethod.argTypes[i].Index] = paramArray;
}
else
......@@ -154,44 +120,102 @@ namespace NLua.Method
!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
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));
}
failedCall = false;
}
catch (TargetInvocationException e)
{
// Failure of method invocation
if (_translator.interpreter.UseTraceback) e.GetBaseException().Data["Traceback"] = _translator.interpreter.GetDebugTraceback();
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);
}
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);
}
// Cache miss
if (failedCall)
int CallMethodFromName(LuaState luaState)
{
// System.Diagnostics.Debug.WriteLine("cache miss on " + methodName);
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 (!_isStatic)
{
if (targetObject == null)
{
_translator.ThrowError(luaState, string.Format("instance method '{0}' requires a non null target object", _methodName));
_translator.ThrowError(luaState,
string.Format("instance method '{0}' requires a non null target object", _methodName));
luaState.PushNil();
return 1;
}
......@@ -204,8 +228,11 @@ namespace NLua.Method
foreach (var member in _members)
{
if (member.ReflectedType == null)
continue;
candidateName = member.ReflectedType.Name + "." + member.Name;
bool isMethod = _translator.MatchParameters(luaState, member, ref _lastCalledMethod);
bool isMethod = _translator.MatchParameters(luaState, member, _lastCalledMethod, 0);
if (isMethod)
{
......@@ -216,39 +243,56 @@ namespace NLua.Method
if (!hasMatch)
{
string msg = (candidateName == null) ? "Invalid arguments to method call" : ("Invalid arguments to method: " + candidateName);
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);
}
}
else
{ // Method from MethodBase instance
if (methodToCall.ContainsGenericParameters)
{
_translator.MatchParameters(luaState, methodToCall, ref _lastCalledMethod);
if (methodToCall.IsGenericMethodDefinition)
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 = ((MethodInfo)methodToCall).MakeGenericMethod(typeArgs.ToArray());
var concreteMethod = methodToCall.MakeGenericMethod(typeArgs.ToArray());
_translator.Push(luaState, concreteMethod.Invoke(targetObject, _lastCalledMethod.args));
failedCall = false;
return PushReturnValue(luaState);
}
else if (methodToCall.ContainsGenericParameters)
/*
* Calls the method. Receives the arguments from the Lua stack
* and returns values in it.
*/
int Call(IntPtr state)
{
_translator.ThrowError(luaState, "Unable to invoke method on generic class as the current method is an open generic method");
luaState.PushNil();
return 1;
}
}
else
var luaState = LuaState.FromIntPtr(state);
MethodBase methodToCall = _method;
object targetObject = _target;
if (!luaState.CheckStack(5))
throw new LuaException("Lua stack overflow");
SetPendingException(null);
// Method from name
if (methodToCall == null)
return CallMethodFromName(luaState);
// Method from MethodBase instance
if (!methodToCall.ContainsGenericParameters)
{
if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null)
{
......@@ -256,58 +300,30 @@ namespace NLua.Method
luaState.Remove(1); // Pops the receiver
}
if (!_translator.MatchParameters(luaState, methodToCall, ref _lastCalledMethod))
if (!_translator.MatchParameters(luaState, methodToCall, _lastCalledMethod, 0))
{
_translator.ThrowError(luaState, "Invalid arguments to method call");
luaState.PushNil();
return 1;
}
}
}
if (failedCall)
{
if (!luaState.CheckStack(_lastCalledMethod.outList.Length + 6))
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();
return SetPendingException(e.GetBaseException());
}
catch (Exception e)
if (!methodToCall.IsGenericMethodDefinition)
{
return SetPendingException(e);
}
_translator.ThrowError(luaState,
"Unable to invoke method on generic class as the current method is an open generic method");
luaState.PushNil();
return 1;
}
// Pushes out and ref return values
for (int index = 0; index < _lastCalledMethod.outList.Length; index++)
{
nReturnValues++;
_translator.Push(luaState, _lastCalledMethod.args[_lastCalledMethod.outList[index]]);
return CallInvokeOnGenericMethod(luaState, (MethodInfo) methodToCall, targetObject);
}
if (_isStatic)
targetObject = null;
// 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;
return CallInvoke(luaState, _lastCalledMethod.cachedMethod, targetObject);
}
}
}
\ No newline at end of file
......@@ -5,13 +5,14 @@ namespace NLua.Method
/*
* Parameter information
*/
struct MethodArgs
class MethodArgs
{
// Position of parameter
public int Index;
public Type ParameterType;
// Type-conversion function
public ExtractValue ExtractValue;
public bool IsParamsArray;
public Type ParamsArrayType;
}
}
\ No newline at end of file
......@@ -4,8 +4,14 @@ using NLua.Extensions;
namespace NLua.Method
{
struct MethodCache
class MethodCache
{
public MethodCache()
{
args = new object[0];
argTypes = new MethodArgs[0];
outList = new int[0];
}
private MethodBase _cachedMethod;
public MethodBase cachedMethod {
......
......@@ -331,9 +331,10 @@ namespace NLua
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)
......@@ -1009,9 +1010,9 @@ namespace NLua
* Checks if the method matches the arguments in the Lua stack, getting
* 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)
......
......@@ -188,8 +188,11 @@ namespace NLuaTest
lua.DoString("test=TestClass()");
lua.DoString("test:MethodOverload()");
lua.DoString("test:MethodOverload(test)");
lua.DoString("test:MethodOverload(test)");
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
lua.DoString("test=TestClass()");
lua.DoString("setMethod=luanet.get_method_bysig(TestClass,'setVal','System.String')");
lua.DoString("setMethod(test,'test')");
TestTypes.TestClass test = (TestTypes.TestClass)lua["test"];
var test = (TestTypes.TestClass)lua["test"];
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