/* * This file is part of LuaInterface. * * Copyright (C) 2003-2005 Fabio Mascarenhas de Queiroz. * Copyright (C) 2012 Megax * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; using System.Reflection; using System.Collections.Generic; using LuaInterface.Exceptions; using LuaInterface.Extensions; namespace LuaInterface.Method { using LuaCore = KeraLua.Lua; /* * Argument extraction with type-conversion function */ delegate object ExtractValue (LuaCore.lua_State luaState, int stackPos); /* * Wrapper class for methods/constructors accessed from Lua. * * Author: Fabio Mascarenhas * Version: 1.0 */ class LuaMethodWrapper { private ObjectTranslator _Translator; private MethodBase _Method; private MethodCache _LastCalledMethod = new MethodCache (); private string _MethodName; private MemberInfo[] _Members; private ExtractValue _ExtractTarget; private object _Target; private BindingFlags _BindingType; /* * Constructs the wrapper for a known MethodBase instance */ public LuaMethodWrapper (ObjectTranslator translator, object target, IReflect targetType, MethodBase method) { _Translator = translator; _Target = target; if (!targetType.IsNull ()) _ExtractTarget = translator.typeChecker.getExtractor (targetType); _Method = method; _MethodName = method.Name; if (method.IsStatic) _BindingType = BindingFlags.Static; else _BindingType = BindingFlags.Instance; } /* * Constructs the wrapper for a known method name */ public LuaMethodWrapper (ObjectTranslator translator, IReflect targetType, string methodName, BindingFlags bindingType) { _Translator = translator; _MethodName = methodName; if (!targetType.IsNull ()) _ExtractTarget = translator.typeChecker.getExtractor (targetType); _BindingType = bindingType; //CP: Removed NonPublic binding search and added IgnoreCase _Members = targetType.UnderlyingSystemType.GetMember (methodName, MemberTypes.Method, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*|BindingFlags.NonPublic*/); } /// /// Convert C# exceptions into Lua errors /// /// num of things on stack /// null for no pending exception int SetPendingException (Exception e) { return _Translator.interpreter.SetPendingException (e); } /* * Calls the method. Receives the arguments from the Lua stack * and returns values in it. */ #if MONOTOUCH [MonoTouch.MonoPInvokeCallback (typeof (Lua.lua_CFunction))] #endif [System.Runtime.InteropServices.AllowReversePInvokeCalls] public int call (LuaCore.lua_State luaState) { var methodToCall = _Method; object targetObject = _Target; bool failedCall = true; int nReturnValues = 0; if (!LuaLib.lua_checkstack (luaState, 5)) throw new LuaException ("Lua stack overflow"); bool isStatic = (_BindingType & BindingFlags.Static) == BindingFlags.Static; SetPendingException (null); if (methodToCall.IsNull ()) { // Method from name if (isStatic) targetObject = null; else targetObject = _ExtractTarget (luaState, 1); //LuaLib.lua_remove(luaState,1); // Pops the receiver if (!_LastCalledMethod.cachedMethod.IsNull ()) { // 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 = LuaLib.lua_gettop (luaState) - numStackToSkip; if (numArgsPassed == _LastCalledMethod.argTypes.Length) { // No. of args match? if (!LuaLib.lua_checkstack (luaState, _LastCalledMethod.outList.Length + 6)) throw new LuaException ("Lua stack overflow"); try { for (int i = 0; i < _LastCalledMethod.argTypes.Length; i++) { if (_LastCalledMethod.argTypes [i].isParamsArray) { object luaParamValue = _LastCalledMethod.argTypes [i].extractValue (luaState, i + 1 + numStackToSkip); var paramArrayType = _LastCalledMethod.argTypes [i].paramsArrayType; Array paramArray; if (luaParamValue is LuaTable) { var table = (LuaTable)luaParamValue; paramArray = Array.CreateInstance (paramArrayType, table.Values.Count); for (int x = 1; x <= table.Values.Count; x++) paramArray.SetValue (Convert.ChangeType (table [x], paramArrayType), x - 1); } else { paramArray = Array.CreateInstance (paramArrayType, 1); paramArray.SetValue (luaParamValue, 0); } _LastCalledMethod.args [_LastCalledMethod.argTypes [i].index] = paramArray; } else { _LastCalledMethod.args [_LastCalledMethod.argTypes [i].index] = _LastCalledMethod.argTypes [i].extractValue (luaState, i + 1 + numStackToSkip); } if (_LastCalledMethod.args [_LastCalledMethod.argTypes [i].index] == null && !LuaLib.lua_isnil (luaState, i + 1 + numStackToSkip)) throw new LuaException ("argument number " + (i + 1) + " is invalid"); } if ((_BindingType & BindingFlags.Static) == BindingFlags.Static) _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)); } failedCall = false; } catch (TargetInvocationException e) { // Failure of method invocation return SetPendingException (e.GetBaseException ()); } catch (Exception e) { if (_Members.Length == 1) // Is the method overloaded? // No, throw error return SetPendingException (e); } } } // Cache miss if (failedCall) { // System.Diagnostics.Debug.WriteLine("cache miss on " + methodName); // If we are running an instance variable, we can now pop the targetObject from the stack if (!isStatic) { if (targetObject.IsNull ()) { _Translator.throwError (luaState, String.Format ("instance method '{0}' requires a non null target object", _MethodName)); LuaLib.lua_pushnil (luaState); return 1; } LuaLib.lua_remove (luaState, 1); // Pops the receiver } bool hasMatch = false; string candidateName = null; foreach (var member in _Members) { candidateName = member.ReflectedType.Name + "." + member.Name; var m = (MethodInfo)member; bool isMethod = _Translator.matchParameters (luaState, m, ref _LastCalledMethod); if (isMethod) { hasMatch = true; break; } } if (!hasMatch) { string msg = (candidateName == null) ? "invalid arguments to method call" : ("invalid arguments to method: " + candidateName); _Translator.throwError (luaState, msg); LuaLib.lua_pushnil (luaState); return 1; } } } else { // Method from MethodBase instance if (methodToCall.ContainsGenericParameters) { /*bool isMethod = */ _Translator.matchParameters (luaState, methodToCall, ref _LastCalledMethod); if (methodToCall.IsGenericMethodDefinition) { //need to make a concrete type of the generic method definition var typeArgs = new List (); foreach (object arg in _LastCalledMethod.args) typeArgs.Add (arg.GetType ()); var concreteMethod = (methodToCall as MethodInfo).MakeGenericMethod (typeArgs.ToArray ()); _Translator.push (luaState, concreteMethod.Invoke (targetObject, _LastCalledMethod.args)); failedCall = false; } else if (methodToCall.ContainsGenericParameters) { _Translator.throwError (luaState, "unable to invoke method on generic class as the current method is an open generic method"); LuaLib.lua_pushnil (luaState); return 1; } } else { if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null) { targetObject = _ExtractTarget (luaState, 1); LuaLib.lua_remove (luaState, 1); // Pops the receiver } if (!_Translator.matchParameters (luaState, methodToCall, ref _LastCalledMethod)) { _Translator.throwError (luaState, "invalid arguments to method call"); LuaLib.lua_pushnil (luaState); return 1; } } } if (failedCall) { if (!LuaLib.lua_checkstack (luaState, _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) { return SetPendingException (e.GetBaseException ()); } catch (Exception e) { return SetPendingException (e); } } // Pushes out and ref return values for (int index = 0; index < _LastCalledMethod.outList.Length; index++) { nReturnValues++; //for(int i=0;i 0) nReturnValues++; return nReturnValues < 1 ? 1 : nReturnValues; } } }