123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955 |
- 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;
- }
-
-
- /// <summary>
- /// Debug tool to dump the lua stack
- /// </summary>
- /// 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;
- }
-
-
- /// <summary>
- /// Does this method exist as either an instance or static?
- /// </summary>
- /// <param name="objType"></param>
- /// <param name="methodName"></param>
- /// <returns></returns>
- 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;
- }
-
- /// <summary>
- /// Tries to set a named property or field
- /// </summary>
- /// <param name="luaState"></param>
- /// <param name="targetType"></param>
- /// <param name="target"></param>
- /// <param name="bindingType"></param>
- /// <returns>false if unable to find the named member, true for success</returns>
- 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;
- }
-
- /// <summary>
- /// Convert a C# exception into a Lua error
- /// </summary>
- /// <param name="e"></param>
- /// 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<int> outList = new List<int>();
- List<MethodArgs> argTypes = new List<MethodArgs>();
- 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;
- }
-
- /// <summary>
- /// CP: Fix for operator overloading failure
- /// Returns true if the type is set and assigns the extract value
- /// </summary>
- /// <param name="luaState"></param>
- /// <param name="currentLuaParam"></param>
- /// <param name="currentNetParam"></param>
- /// <param name="extractValue"></param>
- /// <returns></returns>
- 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;
- }
- }
- }
|