namespace LuaInterface
{
using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
using System.Runtime.InteropServices;
/*
* Functions used in the metatables of userdata representing
* CLR objects
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
public class MetaFunctions
{
/*
* __index metafunction for CLR objects. Implemented in Lua.
*/
internal static string luaIndexFunction =
@"
local function index(obj,name)
local meta=getmetatable(obj)
local cached=meta.cache[name]
if cached then
return cached
else
local value,isFunc = get_object_member(obj,name)
if value==nil and type(isFunc)=='string' then error(isFunc,2) end
if isFunc then
meta.cache[name]=value
end
return value
end
end
return index";
private ObjectTranslator translator;
private Hashtable memberCache = new Hashtable();
internal LuaCSFunction gcFunction, indexFunction, newindexFunction,
baseIndexFunction, classIndexFunction, classNewindexFunction,
execDelegateFunction, callConstructorFunction, toStringFunction;
public MetaFunctions(ObjectTranslator translator)
{
this.translator = translator;
gcFunction = new LuaCSFunction(collectObject);
toStringFunction = new LuaCSFunction(toString);
indexFunction = new LuaCSFunction(getMethod);
newindexFunction = new LuaCSFunction(setFieldOrProperty);
baseIndexFunction = new LuaCSFunction(getBaseMethod);
callConstructorFunction = new LuaCSFunction(callConstructor);
classIndexFunction = new LuaCSFunction(getClassMethod);
classNewindexFunction = new LuaCSFunction(setClassFieldOrProperty);
execDelegateFunction = new LuaCSFunction(runFunctionDelegate);
}
/*
* __call metafunction of CLR delegates, retrieves and calls the delegate.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int runFunctionDelegate(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
LuaCSFunction func = (LuaCSFunction)translator.getRawNetObject(luaState, 1);
LuaDLL.lua_remove(luaState, 1);
return func(luaState);
}
/*
* __gc metafunction of CLR objects.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int collectObject(IntPtr luaState)
{
int udata = LuaDLL.luanet_rawnetobj(luaState, 1);
if (udata != -1)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
translator.collectObject(udata);
}
else
{
// Debug.WriteLine("not found: " + udata);
}
return 0;
}
/*
* __tostring metafunction of CLR objects.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int toString(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
object obj = translator.getRawNetObject(luaState, 1);
if (obj != null)
{
translator.push(luaState, obj.ToString() + ": " + obj.GetHashCode());
}
else LuaDLL.lua_pushnil(luaState);
return 1;
}
///
/// Debug tool to dump the lua stack
///
/// FIXME, move somewhere else
public static void dumpStack(ObjectTranslator translator, IntPtr luaState)
{
int depth = LuaDLL.lua_gettop(luaState);
Debug.WriteLine("lua stack depth: " + depth);
for (int i = 1; i <= depth; i++)
{
LuaTypes type = LuaDLL.lua_type(luaState, i);
// we dump stacks when deep in calls, calling typename while the stack is in flux can fail sometimes, so manually check for key types
string typestr = (type == LuaTypes.LUA_TTABLE) ? "table" : LuaDLL.lua_typename(luaState, type);
string strrep = LuaDLL.lua_tostring(luaState, i);
if (type == LuaTypes.LUA_TUSERDATA)
{
object obj = translator.getRawNetObject(luaState, i);
strrep = obj.ToString();
}
Debug.WriteLine(String.Format("{0}: ({1}) {2}", i, typestr, strrep));
}
}
/*
* Called by the __index metafunction of CLR objects in case the
* method is not cached or it is a field/property/event.
* Receives the object and the member name as arguments and returns
* either the value of the member or a delegate to call it.
* If the member does not exist returns nil.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int getMethod(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
object obj = translator.getRawNetObject(luaState, 1);
if (obj == null)
{
translator.throwError(luaState, "trying to index an invalid object reference");
LuaDLL.lua_pushnil(luaState);
return 1;
}
object index = translator.getObject(luaState, 2);
//Type indexType = index.GetType(); //* not used
string methodName = index as string; // will be null if not a string arg
Type objType = obj.GetType();
// 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 (methodName != null && translator.metaFunctions.isMemberPresent(objType, methodName))
return translator.metaFunctions.getMember(luaState, objType, obj, methodName, BindingFlags.Instance | BindingFlags.IgnoreCase);
}
catch { }
bool failed = true;
// 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);
Array aa = obj as Array;
if (intIndex >= aa.Length) {
return translator.pushError(luaState,"array index out of bounds: "+intIndex + " " + aa.Length);
}
object val = aa.GetValue(intIndex);
translator.push (luaState,val);
failed = false;
}
else
{
// Try to use get_Item to index into this .net object
//MethodInfo getter = objType.GetMethod("get_Item");
// issue here is that there may be multiple indexers..
MethodInfo[] methods = objType.GetMethods();
foreach (MethodInfo mInfo in methods)
{
if (mInfo.Name == "get_Item")
{
//check if the signature matches the input
if (mInfo.GetParameters().Length == 1)
{
MethodInfo getter = mInfo;
ParameterInfo[] actualParms = (getter != null) ? getter.GetParameters() : null;
if (actualParms == null || actualParms.Length != 1)
{
return translator.pushError(luaState, "method not found (or no indexer): " + index);
}
else
{
// Get the index in a form acceptable to the getter
index = translator.getAsType(luaState, 2, actualParms[0].ParameterType);
// Just call the indexer - if out of bounds an exception will happen
try
{
object result = getter.Invoke(obj, new object[]{index});
translator.push(luaState, result);
failed = false;
}
catch (TargetInvocationException e)
{
// Provide a more readable description for the common case of key not found
if (e.InnerException is KeyNotFoundException)
return translator.pushError(luaState, "key '" + index + "' not found ");
else
return translator.pushError(luaState, "exception indexing '" + index + "' " + e.Message);
}
}
}
}
}
}
if (failed) {
return translator.pushError(luaState,"cannot find " + index);
}
LuaDLL.lua_pushboolean(luaState, false);
return 2;
}
/*
* __index metafunction of base classes (the base field of Lua tables).
* Adds a prefix to the method name to call the base version of the method.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int getBaseMethod(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
object obj = translator.getRawNetObject(luaState, 1);
if (obj == null)
{
translator.throwError(luaState, "trying to index an invalid object reference");
LuaDLL.lua_pushnil(luaState);
LuaDLL.lua_pushboolean(luaState, false);
return 2;
}
string methodName = LuaDLL.lua_tostring(luaState, 2);
if (methodName == null)
{
LuaDLL.lua_pushnil(luaState);
LuaDLL.lua_pushboolean(luaState, false);
return 2;
}
translator.metaFunctions.getMember(luaState, obj.GetType(), obj, "__luaInterface_base_" + methodName, BindingFlags.Instance | BindingFlags.IgnoreCase);
LuaDLL.lua_settop(luaState, -2);
if (LuaDLL.lua_type(luaState, -1) == LuaTypes.LUA_TNIL)
{
LuaDLL.lua_settop(luaState, -2);
return translator.metaFunctions.getMember(luaState, obj.GetType(), obj, methodName, BindingFlags.Instance | BindingFlags.IgnoreCase);
}
LuaDLL.lua_pushboolean(luaState, false);
return 2;
}
///
/// Does this method exist as either an instance or static?
///
///
///
///
bool isMemberPresent(IReflect objType, string methodName)
{
object cachedMember = checkMemberCache(memberCache, objType, methodName);
if (cachedMember != null)
return true;
//CP: Removed NonPublic binding search
MemberInfo[] members = objType.GetMember(methodName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase/* | BindingFlags.NonPublic*/);
return (members.Length > 0);
}
/*
* Pushes the value of a member or a delegate to call it, depending on the type of
* the member. Works with static or instance members.
* Uses reflection to find members, and stores the reflected MemberInfo object in
* a cache (indexed by the type of the object and the name of the member).
*/
private int getMember(IntPtr luaState, IReflect objType, object obj, string methodName, BindingFlags bindingType)
{
bool implicitStatic = false;
MemberInfo member = null;
object cachedMember = checkMemberCache(memberCache, objType, methodName);
//object cachedMember=null;
if (cachedMember is LuaCSFunction)
{
translator.pushFunction(luaState, (LuaCSFunction)cachedMember);
translator.push(luaState, true);
return 2;
}
else if (cachedMember != null)
{
member = (MemberInfo)cachedMember;
}
else
{
//CP: Removed NonPublic binding search
MemberInfo[] members = objType.GetMember(methodName, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*| BindingFlags.NonPublic*/);
if (members.Length > 0)
member = members[0];
else
{
// If we can't find any suitable instance members, try to find them as statics - but we only want to allow implicit static
// lookups for fields/properties/events -kevinh
//CP: Removed NonPublic binding search and made case insensitive
members = objType.GetMember(methodName, bindingType | BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase/*| BindingFlags.NonPublic*/);
if (members.Length > 0)
{
member = members[0];
implicitStatic = true;
}
}
}
if (member != null)
{
if (member.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)member;
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
try
{
translator.push(luaState, field.GetValue(obj));
}
catch
{
LuaDLL.lua_pushnil(luaState);
}
}
else if (member.MemberType == MemberTypes.Property)
{
PropertyInfo property = (PropertyInfo)member;
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
try
{
object val = property.GetValue(obj, null);
translator.push(luaState, val);
}
catch (ArgumentException)
{
// If we can't find the getter in our class, recurse up to the base class and see
// if they can help.
if (objType is Type && !(((Type)objType) == typeof(object)))
return getMember(luaState, ((Type)objType).BaseType, obj, methodName, bindingType);
else
LuaDLL.lua_pushnil(luaState);
}
catch (TargetInvocationException e) // Convert this exception into a Lua error
{
ThrowError(luaState, e);
LuaDLL.lua_pushnil(luaState);
}
}
else if (member.MemberType == MemberTypes.Event)
{
EventInfo eventInfo = (EventInfo)member;
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
translator.push(luaState, new RegisterEventHandler(translator.pendingEvents, obj, eventInfo));
}
else if (!implicitStatic)
{
if (member.MemberType == MemberTypes.NestedType)
{
// kevinh - added support for finding nested types
// cache us
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
// Find the name of our class
string name = member.Name;
Type dectype = member.DeclaringType;
// Build a new long name and try to find the type by name
string longname = dectype.FullName + "+" + name;
Type nestedType = translator.FindType(longname);
translator.pushType(luaState, nestedType);
}
else
{
// Member type must be 'method'
LuaCSFunction wrapper = new LuaCSFunction((new LuaMethodWrapper(translator, objType, methodName, bindingType)).call);
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, wrapper);
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);
LuaDLL.lua_pushnil(luaState);
}
}
else
{
// kevinh - we want to throw an exception because meerly 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);
LuaDLL.lua_pushnil(luaState);
}
// push false because we are NOT returning a function (see luaIndexFunction)
translator.push(luaState, false);
return 2;
}
/*
* Checks if a MemberInfo object is cached, returning it or null.
*/
private object checkMemberCache(Hashtable memberCache, IReflect objType, string memberName)
{
Hashtable members = (Hashtable)memberCache[objType];
if (members != null)
return members[memberName];
else
return null;
}
/*
* Stores a MemberInfo object in the member cache.
*/
private void setMemberCache(Hashtable memberCache, IReflect objType, string memberName, object member)
{
Hashtable members = (Hashtable)memberCache[objType];
if (members == null)
{
members = new Hashtable();
memberCache[objType] = members;
}
members[memberName] = member;
}
/*
* __newindex metafunction of CLR objects. Receives the object,
* the member name and the value to be stored as arguments. Throws
* and error if the assignment is invalid.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int setFieldOrProperty(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
object target = translator.getRawNetObject(luaState, 1);
if (target == null)
{
translator.throwError(luaState, "trying to index and invalid object reference");
return 0;
}
Type type = target.GetType();
// First try to look up the parameter as a property name
string detailMessage;
bool didMember = translator.metaFunctions.trySetMember(luaState, type, target, BindingFlags.Instance | BindingFlags.IgnoreCase, out detailMessage);
if (didMember)
return 0; // Must have found the property name
// We didn't find a property name, now see if we can use a [] style this accessor to set array contents
try
{
if (type.IsArray && LuaDLL.lua_isnumber(luaState, 2))
{
int index = (int)LuaDLL.lua_tonumber(luaState, 2);
Array arr = (Array)target;
object val = translator.getAsType(luaState, 3, arr.GetType().GetElementType());
arr.SetValue(val, index);
}
else
{
// Try to see if we have a this[] accessor
MethodInfo setter = type.GetMethod("set_Item");
if (setter != null)
{
ParameterInfo[] args = setter.GetParameters();
Type valueType = args[1].ParameterType;
// The new val ue the user specified
object val = translator.getAsType(luaState, 3, valueType);
Type indexType = args[0].ParameterType;
object index = translator.getAsType(luaState, 2, indexType);
object[] methodArgs = new object[2];
// Just call the indexer - if out of bounds an exception will happen
methodArgs[0] = index;
methodArgs[1] = val;
setter.Invoke(target, methodArgs);
}
else
{
translator.throwError(luaState, detailMessage); // Pass the original message from trySetMember because it is probably best
}
}
}
catch (SEHException)
{
// If we are seeing a C++ exception - this must actually be for Lua's private use. Let it handle it
throw;
}
catch (Exception e)
{
translator.metaFunctions.ThrowError(luaState, e);
}
return 0;
}
///
/// Tries to set a named property or field
///
///
///
///
///
/// false if unable to find the named member, true for success
private bool trySetMember(IntPtr luaState, IReflect targetType, object target, BindingFlags bindingType, out string detailMessage)
{
detailMessage = null; // No error yet
// If not already a string just return - we don't want to call tostring - which has the side effect of
// changing the lua typecode to string
// Note: We don't use isstring because the standard lua C isstring considers either strings or numbers to
// be true for isstring.
if (LuaDLL.lua_type(luaState, 2) != LuaTypes.LUA_TSTRING)
{
detailMessage = "property names must be strings";
return false;
}
// We only look up property names by string
string fieldName = LuaDLL.lua_tostring(luaState, 2);
if (fieldName == null || fieldName.Length < 1 || !(char.IsLetter(fieldName[0]) || fieldName[0] == '_'))
{
detailMessage = "invalid property name";
return false;
}
// Find our member via reflection or the cache
MemberInfo member = (MemberInfo)checkMemberCache(memberCache, targetType, fieldName);
if (member == null)
{
//CP: Removed NonPublic binding search and made case insensitive
MemberInfo[] members = targetType.GetMember(fieldName, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*| BindingFlags.NonPublic*/);
if (members.Length > 0)
{
member = members[0];
setMemberCache(memberCache, targetType, fieldName, member);
}
else
{
detailMessage = "field or property '" + fieldName + "' does not exist";
return false;
}
}
if (member.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)member;
object val = translator.getAsType(luaState, 3, field.FieldType);
try
{
field.SetValue(target, val);
}
catch (Exception e)
{
ThrowError(luaState, e);
}
// We did a call
return true;
}
else if (member.MemberType == MemberTypes.Property)
{
PropertyInfo property = (PropertyInfo)member;
object val = translator.getAsType(luaState, 3, property.PropertyType);
try
{
property.SetValue(target, val, null);
}
catch (Exception e)
{
ThrowError(luaState, e);
}
// We did a call
return true;
}
detailMessage = "'" + fieldName + "' is not a .net field or property";
return false;
}
/*
* Writes to fields or properties, either static or instance. Throws an error
* if the operation is invalid.
*/
private int setMember(IntPtr luaState, IReflect targetType, object target, BindingFlags bindingType)
{
string detail;
bool success = trySetMember(luaState, targetType, target, bindingType, out detail);
if (!success)
translator.throwError(luaState, detail);
return 0;
}
///
/// Convert a C# exception into a Lua error
///
///
/// We try to look into the exception to give the most meaningful description
void ThrowError(IntPtr luaState, Exception e)
{
// If we got inside a reflection show what really happened
TargetInvocationException te = e as TargetInvocationException;
if (te != null)
e = te.InnerException;
translator.throwError(luaState, e);
}
/*
* __index metafunction of type references, works on static members.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int getClassMethod(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
IReflect klass;
object obj = translator.getRawNetObject(luaState, 1);
if (obj == null || !(obj is IReflect))
{
translator.throwError(luaState, "trying to index an invalid type reference");
LuaDLL.lua_pushnil(luaState);
return 1;
}
else klass = (IReflect)obj;
if (LuaDLL.lua_isnumber(luaState, 2))
{
int size = (int)LuaDLL.lua_tonumber(luaState, 2);
translator.push(luaState, Array.CreateInstance(klass.UnderlyingSystemType, size));
return 1;
}
else
{
string methodName = LuaDLL.lua_tostring(luaState, 2);
if (methodName == null)
{
LuaDLL.lua_pushnil(luaState);
return 1;
} //CP: Ignore case
else return translator.metaFunctions.getMember(luaState, klass, null, methodName, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.IgnoreCase);
}
}
/*
* __newindex function of type references, works on static members.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int setClassFieldOrProperty(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
IReflect target;
object obj = translator.getRawNetObject(luaState, 1);
if (obj == null || !(obj is IReflect))
{
translator.throwError(luaState, "trying to index an invalid type reference");
return 0;
}
else target = (IReflect)obj;
return translator.metaFunctions.setMember(luaState, target, null, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.IgnoreCase);
}
/*
* __call metafunction of type references. Searches for and calls
* a constructor for the type. Returns nil if the constructor is not
* found or if the arguments are invalid. Throws an error if the constructor
* generates an exception.
*/
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int callConstructor(IntPtr luaState)
{
ObjectTranslator translator = ObjectTranslator.FromState(luaState);
MethodCache validConstructor = new MethodCache();
IReflect klass;
object obj = translator.getRawNetObject(luaState, 1);
if (obj == null || !(obj is IReflect))
{
translator.throwError(luaState, "trying to call constructor on an invalid type reference");
LuaDLL.lua_pushnil(luaState);
return 1;
}
else klass = (IReflect)obj;
LuaDLL.lua_remove(luaState, 1);
ConstructorInfo[] constructors = klass.UnderlyingSystemType.GetConstructors();
foreach (ConstructorInfo constructor in constructors)
{
bool isConstructor = translator.metaFunctions.matchParameters(luaState, constructor, ref validConstructor);
if (isConstructor)
{
try
{
translator.push(luaState, constructor.Invoke(validConstructor.args));
}
catch (TargetInvocationException e)
{
translator.metaFunctions.ThrowError(luaState, e);
LuaDLL.lua_pushnil(luaState);
}
catch
{
LuaDLL.lua_pushnil(luaState);
}
return 1;
}
}
string constructorName = (constructors.Length == 0) ? "unknown" : constructors[0].Name;
translator.throwError(luaState, String.Format("{0} does not contain constructor({1}) argument match",
klass.UnderlyingSystemType,
constructorName));
LuaDLL.lua_pushnil(luaState);
return 1;
}
private static bool IsInteger(double x) {
return Math.Ceiling(x) == x;
}
internal Array TableToArray(object luaParamValue, Type paramArrayType) {
Array paramArray;
if (luaParamValue is LuaTable) {
LuaTable table = (LuaTable)luaParamValue;
IDictionaryEnumerator tableEnumerator = table.GetEnumerator();
tableEnumerator.Reset();
paramArray = Array.CreateInstance(paramArrayType, table.Values.Count);
int paramArrayIndex = 0;
while(tableEnumerator.MoveNext()) {
object o = tableEnumerator.Value;
if (paramArrayType == typeof(object)) {
if (o != null && o.GetType() == typeof(double) && IsInteger((double)o))
o = Convert.ToInt32((double)o);
}
paramArray.SetValue(Convert.ChangeType(o, paramArrayType), paramArrayIndex);
paramArrayIndex++;
}
} else {
paramArray = Array.CreateInstance(paramArrayType, 1);
paramArray.SetValue(luaParamValue, 0);
}
return paramArray;
}
/*
* Matches a method against its arguments in the Lua stack. Returns
* if the match was succesful. It it was also returns the information
* necessary to invoke the method.
*/
internal bool matchParameters(IntPtr luaState, MethodBase method, ref MethodCache methodCache)
{
ExtractValue extractValue;
bool isMethod = true;
ParameterInfo[] paramInfo = method.GetParameters();
int currentLuaParam = 1;
int nLuaParams = LuaDLL.lua_gettop(luaState);
ArrayList paramList = new ArrayList();
List outList = new List();
List argTypes = new List();
foreach (ParameterInfo currentNetParam in paramInfo)
{
if (!currentNetParam.IsIn && currentNetParam.IsOut) // Skips out params
{
outList.Add(paramList.Add(null));
}
else if (currentLuaParam > nLuaParams) // Adds optional parameters
{
if (currentNetParam.IsOptional)
{
paramList.Add(currentNetParam.DefaultValue);
}
else
{
isMethod = false;
break;
}
}
else if (_IsTypeCorrect(luaState, currentLuaParam, currentNetParam, out extractValue)) // Type checking
{
int index = paramList.Add(extractValue(luaState, currentLuaParam));
MethodArgs methodArg = new MethodArgs();
methodArg.index = index;
methodArg.extractValue = extractValue;
argTypes.Add(methodArg);
if (currentNetParam.ParameterType.IsByRef)
outList.Add(index);
currentLuaParam++;
} // Type does not match, ignore if the parameter is optional
else if (_IsParamsArray(luaState, currentLuaParam, currentNetParam, out extractValue))
{
object luaParamValue = extractValue(luaState, currentLuaParam);
Type paramArrayType = currentNetParam.ParameterType.GetElementType();
Array paramArray = TableToArray(luaParamValue, paramArrayType);
int index = paramList.Add(paramArray);
MethodArgs methodArg = new MethodArgs();
methodArg.index = index;
methodArg.extractValue = extractValue;
methodArg.isParamsArray = true;
methodArg.paramsArrayType = paramArrayType;
argTypes.Add(methodArg);
currentLuaParam++;
}
else if (currentNetParam.IsOptional)
{
paramList.Add(currentNetParam.DefaultValue);
}
else // No match
{
isMethod = false;
break;
}
}
if (currentLuaParam != nLuaParams + 1) // Number of parameters does not match
isMethod = false;
if (isMethod)
{
methodCache.args = paramList.ToArray();
methodCache.cachedMethod = method;
methodCache.outList = outList.ToArray();
methodCache.argTypes = argTypes.ToArray();
}
return isMethod;
}
///
/// CP: Fix for operator overloading failure
/// Returns true if the type is set and assigns the extract value
///
///
///
///
///
///
private bool _IsTypeCorrect(IntPtr luaState, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue)
{
try
{
return (extractValue = translator.typeChecker.checkType(luaState, currentLuaParam, currentNetParam.ParameterType)) != null;
}
catch
{
extractValue = null;
Debug.WriteLine("Type wasn't correct");
return false;
}
}
private bool _IsParamsArray(IntPtr luaState, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue)
{
extractValue = null;
if (currentNetParam.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0)
{
LuaTypes luaType;
try
{
luaType = LuaDLL.lua_type(luaState, currentLuaParam);
}
catch (Exception ex)
{
Debug.WriteLine("Could not retrieve lua type while attempting to determine params Array Status."+ ex.ToString());
Debug.WriteLine(ex.Message);
extractValue = null;
return false;
}
if (luaType == LuaTypes.LUA_TTABLE)
{
try
{
extractValue = translator.typeChecker.getExtractor(typeof(LuaTable));
}
catch (Exception ex)
{
Debug.WriteLine("An error occurred during an attempt to retrieve a LuaTable extractor while checking for params array status." + ex.ToString());
}
if (extractValue != null)
{
return true;
}
}
else
{
Type paramElementType = currentNetParam.ParameterType.GetElementType();
try
{
extractValue = translator.typeChecker.checkType(luaState, currentLuaParam, paramElementType);
}
catch (Exception ex)
{
Debug.WriteLine(string.Format("An error occurred during an attempt to retrieve an extractor ({0}) while checking for params array status:{1}", paramElementType.FullName,ex.ToString()));
}
if (extractValue != null)
{
return true;
}
}
}
Debug.WriteLine("Type wasn't Params object.");
return false;
}
}
}