Unverified Commit 1cc74393 authored by Vinicius Jarina's avatar Vinicius Jarina Committed by GitHub
Browse files

* Giant cleanup/reshuffle of all files. (#265)

* * Giant cleanup/reshuffle of all files.

* * Update upstream `KeraLua` to `0.1.14`

* Fixed .NET Core build.

* Add runsettings file

* * Fixed nuspec `dependencies` node

* Ignore _ in branch names for package names.

* * Fixed nuspec.
parent 3f254585
using System;
using LuaState = KeraLua.Lua;
namespace NLua
{
class ClassGenerator
{
private ObjectTranslator translator;
private Type klass;
public ClassGenerator(ObjectTranslator objTranslator, Type typeClass)
{
translator = objTranslator;
klass = typeClass;
}
public object ExtractGenerated(LuaState luaState, int stackPos)
{
return CodeGeneration.Instance.GetClassInstance(klass, translator.GetTable(luaState, stackPos));
}
}
}
\ No newline at end of file
using System;
using System.Threading;
using System.Reflection;
using NLua.Extensions;
using System.Reflection.Emit;
using System.Collections;
using System.Collections.Generic;
using NLua.Method;
namespace NLua
{
class CodeGeneration
{
private Dictionary<Type, LuaClassType> classCollection = new Dictionary<Type, LuaClassType>();
private Dictionary<Type, Type> delegateCollection = new Dictionary<Type, Type>();
private static readonly CodeGeneration instance = new CodeGeneration();
private AssemblyName assemblyName;
#if !(__IOS__ || __TVOS__ || __WATCHOS__) && !SILVERLIGHT && !NETSTANDARD
private Dictionary<Type, Type> eventHandlerCollection = new Dictionary<Type, Type>();
private Type eventHandlerParent = typeof(LuaEventHandler);
private Type delegateParent = typeof(LuaDelegate);
private Type classHelper = typeof(LuaClassHelper);
private AssemblyBuilder newAssembly;
private ModuleBuilder newModule;
private int luaClassNumber = 1;
#endif
static CodeGeneration()
{
}
private CodeGeneration()
{
// Create an assembly name
assemblyName = new AssemblyName();
assemblyName.Name = "NLua_generatedcode";
// Create a new assembly with one module.
#if NETCOREAPP
newAssembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
newModule = newAssembly.DefineDynamicModule("NLua_generatedcode");
#elif !(__IOS__ || __TVOS__ || __WATCHOS__) && !NETSTANDARD
newAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
newModule = newAssembly.DefineDynamicModule("NLua_generatedcode");
#endif
}
/*
* Singleton instance of the class
*/
public static CodeGeneration Instance {
get { return instance; }
}
/*
* Generates an event handler that calls a Lua function
*/
private Type GenerateEvent(Type eventHandlerType)
{
#if __IOS__ || __TVOS__ || __WATCHOS__
throw new NotImplementedException (" Emit not available on Xamarin.iOS ");
#elif SILVERLIGHT
throw new NotImplementedException(" Emit not available on Silverlight ");
#elif NETSTANDARD
throw new NotImplementedException(" Emit not available on .NET Standard ");
#else
string typeName;
lock (this)
{
typeName = "LuaGeneratedClass" + luaClassNumber.ToString();
luaClassNumber++;
}
// Define a public class in the assembly, called typeName
var myType = newModule.DefineType(typeName, TypeAttributes.Public, eventHandlerParent);
// Defines the handler method. Its signature is void(object, <subclassofEventArgs>)
var paramTypes = new Type[2];
paramTypes[0] = typeof(object);
paramTypes[1] = eventHandlerType;
var returnType = typeof(void);
var handleMethod = myType.DefineMethod("HandleEvent", MethodAttributes.Public | MethodAttributes.HideBySig, returnType, paramTypes);
// Emits the IL for the method. It loads the arguments
// and calls the handleEvent method of the base class
ILGenerator generator = handleMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldarg_2);
var miGenericEventHandler = eventHandlerParent.GetMethod("HandleEvent");
generator.Emit(OpCodes.Call, miGenericEventHandler);
// returns
generator.Emit(OpCodes.Ret);
// creates the new type
return myType.CreateType();
#endif
}
/*
* Generates a type that can be used for instantiating a delegate
* of the provided type, given a Lua function.
*/
private Type GenerateDelegate(Type delegateType)
{
#if __IOS__ || __TVOS__ || __WATCHOS__
throw new NotImplementedException ("GenerateDelegate is not available on iOS, please register your LuaDelegate type with Lua.RegisterLuaDelegateType( yourDelegate, theLuaDelegateHandler) ");
#elif SILVERLIGHT
throw new NotImplementedException("GenerateDelegate is not available on Silverlight, please register your LuaDelegate type with Lua.RegisterLuaDelegateType( yourDelegate, theLuaDelegateHandler) ");
#elif NETSTANDARD
throw new NotImplementedException("GenerateDelegate is not available on Windows Store, please register your LuaDelegate type with Lua.RegisterLuaDelegateType( yourDelegate, theLuaDelegateHandler) ");
#else
string typeName;
lock (this)
{
typeName = "LuaGeneratedClass" + luaClassNumber.ToString();
luaClassNumber++;
}
// Define a public class in the assembly, called typeName
var myType = newModule.DefineType(typeName, TypeAttributes.Public, delegateParent);
// Defines the delegate method with the same signature as the
// Invoke method of delegateType
var invokeMethod = delegateType.GetMethod("Invoke");
var paramInfo = invokeMethod.GetParameters();
var paramTypes = new Type[paramInfo.Length];
var returnType = invokeMethod.ReturnType;
// Counts out and ref params, for use later
int nOutParams = 0;
int nOutAndRefParams = 0;
for (int i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = paramInfo[i].ParameterType;
if ((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
nOutParams++;
if (paramTypes[i].IsByRef)
nOutAndRefParams++;
}
int[] refArgs = new int[nOutAndRefParams];
var delegateMethod = myType.DefineMethod("CallFunction", invokeMethod.Attributes, returnType, paramTypes);
// Generates the IL for the method
ILGenerator generator = delegateMethod.GetILGenerator();
generator.DeclareLocal(typeof(object[])); // original arguments
generator.DeclareLocal(typeof(object[])); // with out-only arguments removed
generator.DeclareLocal(typeof(int[])); // indexes of out and ref arguments
if (!(returnType == typeof(void))) // return value
generator.DeclareLocal(returnType);
else
generator.DeclareLocal(typeof(object));
// Initializes local variables
generator.Emit(OpCodes.Ldc_I4, paramTypes.Length);
generator.Emit(OpCodes.Newarr, typeof(object));
generator.Emit(OpCodes.Stloc_0);
generator.Emit(OpCodes.Ldc_I4, paramTypes.Length - nOutParams);
generator.Emit(OpCodes.Newarr, typeof(object));
generator.Emit(OpCodes.Stloc_1);
generator.Emit(OpCodes.Ldc_I4, nOutAndRefParams);
generator.Emit(OpCodes.Newarr, typeof(int));
generator.Emit(OpCodes.Stloc_2);
// Stores the arguments in the local variables
for (int iArgs = 0, iInArgs = 0, iOutArgs = 0; iArgs < paramTypes.Length; iArgs++)
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldc_I4, iArgs);
generator.Emit(OpCodes.Ldarg, iArgs + 1);
if (paramTypes[iArgs].IsByRef)
{
if (paramTypes[iArgs].GetElementType().IsValueType)
{
generator.Emit(OpCodes.Ldobj, paramTypes[iArgs].GetElementType());
generator.Emit(OpCodes.Box, paramTypes[iArgs].GetElementType());
}
else
generator.Emit(OpCodes.Ldind_Ref);
}
else
{
if (paramTypes[iArgs].IsValueType)
generator.Emit(OpCodes.Box, paramTypes[iArgs]);
}
generator.Emit(OpCodes.Stelem_Ref);
if (paramTypes[iArgs].IsByRef)
{
generator.Emit(OpCodes.Ldloc_2);
generator.Emit(OpCodes.Ldc_I4, iOutArgs);
generator.Emit(OpCodes.Ldc_I4, iArgs);
generator.Emit(OpCodes.Stelem_I4);
refArgs[iOutArgs] = iArgs;
iOutArgs++;
}
if (paramInfo[iArgs].IsIn || (!paramInfo[iArgs].IsOut))
{
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldc_I4, iInArgs);
generator.Emit(OpCodes.Ldarg, iArgs + 1);
if (paramTypes[iArgs].IsByRef)
{
if (paramTypes[iArgs].GetElementType().IsValueType)
{
generator.Emit(OpCodes.Ldobj, paramTypes[iArgs].GetElementType());
generator.Emit(OpCodes.Box, paramTypes[iArgs].GetElementType());
}
else
generator.Emit(OpCodes.Ldind_Ref);
}
else
{
if (paramTypes[iArgs].IsValueType)
generator.Emit(OpCodes.Box, paramTypes[iArgs]);
}
generator.Emit(OpCodes.Stelem_Ref);
iInArgs++;
}
}
// Calls the callFunction method of the base class
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldloc_2);
var miGenericEventHandler = delegateParent.GetMethod("CallFunction");
generator.Emit(OpCodes.Call, miGenericEventHandler);
// Stores return value
if (returnType == typeof(void))
{
generator.Emit(OpCodes.Pop);
generator.Emit(OpCodes.Ldnull);
}
else if (returnType.IsValueType)
{
generator.Emit(OpCodes.Unbox, returnType);
generator.Emit(OpCodes.Ldobj, returnType);
}
else
generator.Emit(OpCodes.Castclass, returnType);
generator.Emit(OpCodes.Stloc_3);
// Stores new value of out and ref params
for (int i = 0; i < refArgs.Length; i++)
{
generator.Emit(OpCodes.Ldarg, refArgs[i] + 1);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldc_I4, refArgs[i]);
generator.Emit(OpCodes.Ldelem_Ref);
if (paramTypes[refArgs[i]].GetElementType().IsValueType)
{
generator.Emit(OpCodes.Unbox, paramTypes[refArgs[i]].GetElementType());
generator.Emit(OpCodes.Ldobj, paramTypes[refArgs[i]].GetElementType());
generator.Emit(OpCodes.Stobj, paramTypes[refArgs[i]].GetElementType());
}
else
{
generator.Emit(OpCodes.Castclass, paramTypes[refArgs[i]].GetElementType());
generator.Emit(OpCodes.Stind_Ref);
}
}
// Returns
if (!(returnType == typeof(void)))
generator.Emit(OpCodes.Ldloc_3);
generator.Emit(OpCodes.Ret);
return myType.CreateType(); // creates the new type
#endif
}
void GetReturnTypesFromClass(Type klass, out Type[][] returnTypes)
{
var classMethods = klass.GetMethods();
returnTypes = new Type[classMethods.Length][];
int i = 0;
foreach (var method in classMethods)
{
if (klass.IsInterface)
{
GetReturnTypesFromMethod(method, out returnTypes[i]);
i++;
}
else
{
if (!method.IsPrivate && !method.IsFinal && method.IsVirtual)
{
GetReturnTypesFromMethod(method, out returnTypes[i]);
i++;
}
}
}
}
/*
* Generates an implementation of klass, if it is an interface, or
* a subclass of klass that delegates its virtual methods to a Lua table.
*/
public void GenerateClass(Type klass, out Type newType, out Type[][] returnTypes)
{
#if __IOS__ || __TVOS__ || __WATCHOS__
throw new NotImplementedException (" Emit not available on Xamarin.iOS ");
#elif SILVERLIGHT
throw new NotImplementedException (" Emit not available on Silverlight ");
#elif NETSTANDARD
throw new NotImplementedException (" Emit not available on .NET Standard ");
#else
string typeName;
lock (this)
{
typeName = "LuaGeneratedClass" + luaClassNumber.ToString();
luaClassNumber++;
}
TypeBuilder myType;
// Define a public class in the assembly, called typeName
if (klass.IsInterface)
myType = newModule.DefineType(typeName, TypeAttributes.Public, typeof(object), new Type[] {
klass,
typeof(ILuaGeneratedType)
});
else
myType = newModule.DefineType(typeName, TypeAttributes.Public, klass, new Type[] { typeof(ILuaGeneratedType) });
// Field that stores the Lua table
var luaTableField = myType.DefineField("__luaInterface_luaTable", typeof(LuaTable), FieldAttributes.Public);
// Field that stores the return types array
var returnTypesField = myType.DefineField("__luaInterface_returnTypes", typeof(Type[][]), FieldAttributes.Public);
// Generates the constructor for the new type, it takes a Lua table and an array
// of return types and stores them in the respective fields
var constructor = myType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] {
typeof(LuaTable),
typeof(Type[][])
});
ILGenerator generator = constructor.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
if (klass.IsInterface)
generator.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
else
generator.Emit(OpCodes.Call, klass.GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Stfld, luaTableField);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_2);
generator.Emit(OpCodes.Stfld, returnTypesField);
generator.Emit(OpCodes.Ret);
// Generates overriden versions of the klass' public virtual methods
var classMethods = klass.GetMethods();
returnTypes = new Type[classMethods.Length][];
int i = 0;
foreach (var method in classMethods)
{
if (klass.IsInterface)
{
GenerateMethod(myType, method, MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot,
i, luaTableField, returnTypesField, false, out returnTypes[i]);
i++;
}
else
{
if (!method.IsPrivate && !method.IsFinal && method.IsVirtual)
{
GenerateMethod(myType, method, (method.Attributes | MethodAttributes.NewSlot) ^ MethodAttributes.NewSlot, i,
luaTableField, returnTypesField, true, out returnTypes[i]);
i++;
}
}
}
// Generates an implementation of the luaInterfaceGetLuaTable method
var returnTableMethod = myType.DefineMethod("LuaInterfaceGetLuaTable",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(LuaTable), new Type[0]);
myType.DefineMethodOverride(returnTableMethod, typeof(ILuaGeneratedType).GetMethod("LuaInterfaceGetLuaTable"));
generator = returnTableMethod.GetILGenerator();
generator.Emit(OpCodes.Ldfld, luaTableField);
generator.Emit(OpCodes.Ret);
newType = myType.CreateType(); // Creates the type
#endif
}
void GetReturnTypesFromMethod(MethodInfo method, out Type[] returnTypes)
{
var paramInfo = method.GetParameters();
var paramTypes = new Type[paramInfo.Length];
var returnTypesList = new List<Type>();
// Counts out and ref parameters, for later use,
// and creates the list of return types
int nOutParams = 0;
int nOutAndRefParams = 0;
var returnType = method.ReturnType;
returnTypesList.Add(returnType);
for (int i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = paramInfo[i].ParameterType;
#if SILVERLIGHT
if (paramInfo[i].IsOut) {
#else
if ((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
{
#endif
nOutParams++;
}
if (paramTypes[i].IsByRef)
{
returnTypesList.Add(paramTypes[i].GetElementType());
nOutAndRefParams++;
}
}
returnTypes = returnTypesList.ToArray();
}
#if !(__IOS__ || __TVOS__ || __WATCHOS__) && !SILVERLIGHT && !NETSTANDARD
/*
* Generates an overriden implementation of method inside myType that delegates
* to a function in a Lua table with the same name, if the function exists. If it
* doesn't the method calls the base method (or does nothing, in case of interface
* implementations).
*/
private void GenerateMethod(TypeBuilder myType, MethodInfo method, MethodAttributes attributes, int methodIndex,
FieldInfo luaTableField, FieldInfo returnTypesField, bool generateBase, out Type[] returnTypes)
{
var paramInfo = method.GetParameters();
var paramTypes = new Type[paramInfo.Length];
var returnTypesList = new List<Type>();
// Counts out and ref parameters, for later use,
// and creates the list of return types
int nOutParams = 0;
int nOutAndRefParams = 0;
var returnType = method.ReturnType;
returnTypesList.Add(returnType);
for (int i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = paramInfo[i].ParameterType;
if ((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
nOutParams++;
if (paramTypes[i].IsByRef)
{
returnTypesList.Add(paramTypes[i].GetElementType());
nOutAndRefParams++;
}
}
int[] refArgs = new int[nOutAndRefParams];
returnTypes = returnTypesList.ToArray();
// Generates a version of the method that calls the base implementation
// directly, for use by the base field of the table
if (generateBase)
{
var baseMethod = myType.DefineMethod("__luaInterface_base_" + method.Name,
MethodAttributes.Private | MethodAttributes.NewSlot | MethodAttributes.HideBySig,
returnType, paramTypes);
ILGenerator generatorBase = baseMethod.GetILGenerator();
generatorBase.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < paramTypes.Length; i++)
generatorBase.Emit(OpCodes.Ldarg, i + 1);
generatorBase.Emit(OpCodes.Call, method);
if (returnType == typeof(void))
generatorBase.Emit(OpCodes.Pop);
generatorBase.Emit(OpCodes.Ret);
}
// Defines the method
var methodImpl = myType.DefineMethod(method.Name, attributes, returnType, paramTypes);
// If it's an implementation of an interface tells what method it
// is overriding
if (myType.BaseType.Equals(typeof(object)))
myType.DefineMethodOverride(methodImpl, method);
ILGenerator generator = methodImpl.GetILGenerator();
generator.DeclareLocal(typeof(object[])); // original arguments
generator.DeclareLocal(typeof(object[])); // with out-only arguments removed
generator.DeclareLocal(typeof(int[])); // indexes of out and ref arguments
if (!(returnType == typeof(void))) // return value
generator.DeclareLocal(returnType);
else
generator.DeclareLocal(typeof(object));
// Initializes local variables
generator.Emit(OpCodes.Ldc_I4, paramTypes.Length);
generator.Emit(OpCodes.Newarr, typeof(object));
generator.Emit(OpCodes.Stloc_0);
generator.Emit(OpCodes.Ldc_I4, paramTypes.Length - nOutParams + 1);
generator.Emit(OpCodes.Newarr, typeof(object));
generator.Emit(OpCodes.Stloc_1);
generator.Emit(OpCodes.Ldc_I4, nOutAndRefParams);
generator.Emit(OpCodes.Newarr, typeof(int));
generator.Emit(OpCodes.Stloc_2);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, luaTableField);
generator.Emit(OpCodes.Stelem_Ref);
// Stores the arguments into the local variables, as needed
for (int iArgs = 0, iInArgs = 1, iOutArgs = 0; iArgs < paramTypes.Length; iArgs++)
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldc_I4, iArgs);
generator.Emit(OpCodes.Ldarg, iArgs + 1);
if (paramTypes[iArgs].IsByRef)
{
if (paramTypes[iArgs].GetElementType().IsValueType)
{
generator.Emit(OpCodes.Ldobj, paramTypes[iArgs].GetElementType());
generator.Emit(OpCodes.Box, paramTypes[iArgs].GetElementType());
}
else
generator.Emit(OpCodes.Ldind_Ref);
}
else
{
if (paramTypes[iArgs].IsValueType)
generator.Emit(OpCodes.Box, paramTypes[iArgs]);
}
generator.Emit(OpCodes.Stelem_Ref);
if (paramTypes[iArgs].IsByRef)
{
generator.Emit(OpCodes.Ldloc_2);
generator.Emit(OpCodes.Ldc_I4, iOutArgs);
generator.Emit(OpCodes.Ldc_I4, iArgs);
generator.Emit(OpCodes.Stelem_I4);
refArgs[iOutArgs] = iArgs;
iOutArgs++;
}
if (paramInfo[iArgs].IsIn || (!paramInfo[iArgs].IsOut))
{
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldc_I4, iInArgs);
generator.Emit(OpCodes.Ldarg, iArgs + 1);
if (paramTypes[iArgs].IsByRef)
{
if (paramTypes[iArgs].GetElementType().IsValueType)
{
generator.Emit(OpCodes.Ldobj, paramTypes[iArgs].GetElementType());
generator.Emit(OpCodes.Box, paramTypes[iArgs].GetElementType());
}
else
generator.Emit(OpCodes.Ldind_Ref);
}
else
{
if (paramTypes[iArgs].IsValueType)
generator.Emit(OpCodes.Box, paramTypes[iArgs]);
}
generator.Emit(OpCodes.Stelem_Ref);
iInArgs++;
}
}
// Gets the function the method will delegate to by calling
// the getTableFunction method of class LuaClassHelper
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, luaTableField);
generator.Emit(OpCodes.Ldstr, method.Name);
generator.Emit(OpCodes.Call, classHelper.GetMethod("GetTableFunction"));
var lab1 = generator.DefineLabel();
generator.Emit(OpCodes.Dup);
generator.Emit(OpCodes.Brtrue_S, lab1);
// Function does not exist, call base method
generator.Emit(OpCodes.Pop);
if (!method.IsAbstract)
{
generator.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < paramTypes.Length; i++)
generator.Emit(OpCodes.Ldarg, i + 1);
generator.Emit(OpCodes.Call, method);
if (returnType == typeof(void))
generator.Emit(OpCodes.Pop);
generator.Emit(OpCodes.Ret);
generator.Emit(OpCodes.Ldnull);
}
else
generator.Emit(OpCodes.Ldnull);
var lab2 = generator.DefineLabel();
generator.Emit(OpCodes.Br_S, lab2);
generator.MarkLabel(lab1);
// Function exists, call using method callFunction of LuaClassHelper
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, returnTypesField);
generator.Emit(OpCodes.Ldc_I4, methodIndex);
generator.Emit(OpCodes.Ldelem_Ref);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldloc_2);
generator.Emit(OpCodes.Call, classHelper.GetMethod("CallFunction"));
generator.MarkLabel(lab2);
// Stores the function return value
if (returnType == typeof(void))
{
generator.Emit(OpCodes.Pop);
generator.Emit(OpCodes.Ldnull);
}
else if (returnType.IsValueType)
{
generator.Emit(OpCodes.Unbox, returnType);
generator.Emit(OpCodes.Ldobj, returnType);
}
else
generator.Emit(OpCodes.Castclass, returnType);
generator.Emit(OpCodes.Stloc_3);
// Sets return values of out and ref parameters
for (int i = 0; i < refArgs.Length; i++)
{
generator.Emit(OpCodes.Ldarg, refArgs[i] + 1);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldc_I4, refArgs[i]);
generator.Emit(OpCodes.Ldelem_Ref);
if (paramTypes[refArgs[i]].GetElementType().IsValueType)
{
generator.Emit(OpCodes.Unbox, paramTypes[refArgs[i]].GetElementType());
generator.Emit(OpCodes.Ldobj, paramTypes[refArgs[i]].GetElementType());
generator.Emit(OpCodes.Stobj, paramTypes[refArgs[i]].GetElementType());
}
else
{
generator.Emit(OpCodes.Castclass, paramTypes[refArgs[i]].GetElementType());
generator.Emit(OpCodes.Stind_Ref);
}
}
// Returns
if (!(returnType == typeof(void)))
generator.Emit(OpCodes.Ldloc_3);
generator.Emit(OpCodes.Ret);
}
#endif
/*
* Gets an event handler for the event type that delegates to the eventHandler Lua function.
* Caches the generated type.
*/
public LuaEventHandler GetEvent(Type eventHandlerType, LuaFunction eventHandler)
{
#if __IOS__ || __TVOS__ || __WATCHOS__
throw new NotImplementedException (" Emit not available on Xamarin.iOS ");
#elif SILVERLIGHT
throw new NotImplementedException (" Emit not available on Silverlight ");
#elif NETSTANDARD
throw new NotImplementedException (" Emit not available on .NET Standard ");
#else
Type eventConsumerType;
if (eventHandlerCollection.ContainsKey(eventHandlerType))
eventConsumerType = eventHandlerCollection[eventHandlerType];
else
{
eventConsumerType = GenerateEvent(eventHandlerType);
eventHandlerCollection[eventHandlerType] = eventConsumerType;
}
var luaEventHandler = (LuaEventHandler)Activator.CreateInstance(eventConsumerType);
luaEventHandler.handler = eventHandler;
return luaEventHandler;
#endif
}
public void RegisterLuaDelegateType(Type delegateType, Type luaDelegateType)
{
delegateCollection[delegateType] = luaDelegateType;
}
public void RegisterLuaClassType(Type klass, Type luaClass)
{
LuaClassType luaClassType = new LuaClassType();
luaClassType.klass = luaClass;
GetReturnTypesFromClass(klass, out luaClassType.returnTypes);
classCollection[klass] = luaClassType;
}
/*
* Gets a delegate with delegateType that calls the luaFunc Lua function
* Caches the generated type.
*/
public Delegate GetDelegate(Type delegateType, LuaFunction luaFunc)
{
var returnTypes = new List<Type>();
Type luaDelegateType;
if (delegateCollection.ContainsKey(delegateType))
luaDelegateType = delegateCollection[delegateType];
else
{
luaDelegateType = GenerateDelegate(delegateType);
delegateCollection[delegateType] = luaDelegateType;
}
var methodInfo = delegateType.GetMethod("Invoke");
returnTypes.Add(methodInfo.ReturnType);
foreach (ParameterInfo paramInfo in methodInfo.GetParameters())
{
if (paramInfo.ParameterType.IsByRef)
returnTypes.Add(paramInfo.ParameterType);
}
var luaDelegate = (LuaDelegate)Activator.CreateInstance(luaDelegateType);
luaDelegate.function = luaFunc;
luaDelegate.returnTypes = returnTypes.ToArray();
#if NETFX_CORE
var mi = luaDelegate.GetType ().GetTypeInfo ().GetDeclaredMethod ("CallFunction");
return mi.CreateDelegate (delegateType, luaDelegate);
#else
return Delegate.CreateDelegate(delegateType, luaDelegate, "CallFunction");
#endif
}
/*
* Gets an instance of an implementation of the klass interface or
* subclass of klass that delegates public virtual methods to the
* luaTable table.
* Caches the generated type.
*/
public object GetClassInstance(Type klass, LuaTable luaTable)
{
LuaClassType luaClassType;
if (classCollection.ContainsKey(klass))
luaClassType = classCollection[klass];
else
{
luaClassType = new LuaClassType();
GenerateClass(klass, out luaClassType.klass, out luaClassType.returnTypes);
classCollection[klass] = luaClassType;
}
return Activator.CreateInstance(luaClassType.klass, new object[] {
luaTable,
luaClassType.returnTypes
});
}
}
}
\ No newline at end of file
using System;
using LuaState = KeraLua.Lua;
namespace NLua
{
class DelegateGenerator
{
private ObjectTranslator translator;
private Type delegateType;
public DelegateGenerator(ObjectTranslator objectTranslator, Type type)
{
translator = objectTranslator;
delegateType = type;
}
public object ExtractGenerated(LuaState luaState, int stackPos)
{
return CodeGeneration.Instance.GetDelegate(delegateType, translator.GetFunction(luaState, stackPos));
}
}
}
\ No newline at end of file
namespace NLua
{
/*
* Common interface for types generated from tables. The method
* returns the table that overrides some or all of the type's methods.
*/
public interface ILuaGeneratedType
{
LuaTable LuaInterfaceGetLuaTable();
}
}
\ No newline at end of file
using System;
namespace NLua
{
/*
* Structure to store a type and the return types of
* its methods (the type of the returned value and out/ref
* parameters).
*/
struct LuaClassType
{
public Type klass;
public Type[][] returnTypes;
}
}
\ No newline at end of file
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using KeraLua;
using NLua.Event;
using NLua.Method;
using NLua.Exceptions;
using NLua.Extensions;
#if __IOS__ || __TVOS__ || __WATCHOS__
using ObjCRuntime;
#endif
using LuaState = KeraLua.Lua;
using LuaNativeFunction = KeraLua.LuaFunction;
namespace NLua
{
public class Lua : IDisposable
{
#region lua debug functions
/// <summary>
/// Event that is raised when an exception occures during a hook call.
/// </summary>
public event EventHandler<HookExceptionEventArgs> HookException;
/// <summary>
/// Event when lua hook callback is called
/// </summary>
/// <remarks>
/// Is only raised if SetDebugHook is called before.
/// </remarks>
public event EventHandler<DebugHookEventArgs> DebugHook;
/// <summary>
/// lua hook calback delegate
/// </summary>
private LuaHookFunction hookCallback = null;
#endregion
#region Globals auto-complete
private readonly List<string> globals = new List<string>();
private bool globalsSorted;
#endregion
private LuaState luaState;
/// <summary>
/// True while a script is being executed
/// </summary>
public bool IsExecuting { get { return executing; } }
public LuaState State => luaState;
private LuaNativeFunction panicCallback;
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;
static string initLuanet =
@"local metatable = {}
local rawget = rawget
local import_type = luanet.import_type
local load_assembly = luanet.load_assembly
luanet.error, luanet.type = error, type
-- Lookup a .NET identifier component.
function metatable:__index(key) -- key is e.g. 'Form'
-- Get the fully-qualified name, e.g. 'System.Windows.Forms.Form'
local fqn = rawget(self,'.fqn')
fqn = ((fqn and fqn .. '.') or '') .. key
-- Try to find either a luanet function or a CLR type
local obj = rawget(luanet,key) or import_type(fqn)
-- If key is neither a luanet function or a CLR type, then it is simply
-- an identifier component.
if obj == nil then
-- It might be an assembly, so we load it too.
pcall(load_assembly,fqn)
obj = { ['.fqn'] = fqn }
setmetatable(obj, metatable)
end
-- Cache this lookup
rawset(self, key, obj)
return obj
end
-- A non-type has been called; e.g. foo = System.Foo()
function metatable:__call(...)
error('No such type: ' .. rawget(self,'.fqn'), 2)
end
-- This is the root of the .NET namespace
luanet['.fqn'] = false
setmetatable(luanet, metatable)
-- Preload the mscorlib assembly
luanet.load_assembly('mscorlib')";
static string clr_package = @"---
--- This lua module provides auto importing of .net classes into a named package.
--- Makes for super easy use of LuaInterface glue
---
--- example:
--- Threading = CLRPackage(""System"", ""System.Threading"")
--- Threading.Thread.Sleep(100)
---
--- Extensions:
--- import() is a version of CLRPackage() which puts the package into a list which is used by a global __index lookup,
--- and thus works rather like C#'s using statement. It also recognizes the case where one is importing a local
--- assembly, which must end with an explicit .dll extension.
--- Alternatively, luanet.namespace can be used for convenience without polluting the global namespace:
--- local sys,sysi = luanet.namespace {'System','System.IO'}
-- sys.Console.WriteLine(""we are at {0}"",sysi.Directory.GetCurrentDirectory())
-- LuaInterface hosted with stock Lua interpreter will need to explicitly require this...
if not luanet then require 'luanet' end
local import_type, load_assembly = luanet.import_type, luanet.load_assembly
local mt = {
--- Lookup a previously unfound class and add it to our table
__index = function(package, classname)
local class = rawget(package, classname)
if class == nil then
class = import_type(package.packageName .. ""."" .. classname)
if class == nil then class = import_type(classname) end
package[classname] = class -- keep what we found around, so it will be shared
end
return class
end
}
function luanet.namespace(ns)
if type(ns) == 'table' then
local res = {}
for i = 1,#ns do
res[i] = luanet.namespace(ns[i])
end
return unpack(res)
end
-- FIXME - table.packageName could instead be a private index (see Lua 13.4.4)
local t = { packageName = ns }
setmetatable(t,mt)
return t
end
local globalMT, packages
local function set_global_mt()
packages = {}
globalMT = {
__index = function(T,classname)
for i,package in ipairs(packages) do
local class = package[classname]
if class then
_G[classname] = class
return class
end
end
end
}
setmetatable(_G, globalMT)
end
--- Create a new Package class
function CLRPackage(assemblyName, packageName)
-- a sensible default...
packageName = packageName or assemblyName
local ok = pcall(load_assembly,assemblyName) -- Make sure our assembly is loaded
return luanet.namespace(packageName)
end
function import (assemblyName, packageName)
if not globalMT then
set_global_mt()
end
if not packageName then
local i = assemblyName:find('%.dll$')
if i then packageName = assemblyName:sub(1,i-1)
else packageName = assemblyName end
end
local t = CLRPackage(assemblyName,packageName)
table.insert(packages,t)
return t
end
function luanet.make_array (tp,tbl)
local arr = tp[#tbl]
for i,v in ipairs(tbl) do
arr:SetValue(v,i-1)
end
return arr
end
function luanet.each(o)
local e = o:GetEnumerator()
return function()
if e:MoveNext() then
return e.Current
end
end
end
";
public bool UseTraceback { get; set; } = false;
#region Globals auto-complete
/// <summary>
/// An alphabetically sorted list of all globals (objects, methods, etc.) externally added to this Lua instance
/// </summary>
/// <remarks>Members of globals are also listed. The formatting is optimized for text input auto-completion.</remarks>
public IEnumerable<string> Globals {
get
{
// Only sort list when necessary
if (!globalsSorted)
{
globals.Sort();
globalsSorted = true;
}
return globals;
}
}
#endregion
public Lua()
{
luaState = new LuaState();
Init();
// We need to keep this in a managed reference so the delegate doesn't get garbage collected
panicCallback = PanicCallback;
luaState.AtPanic(panicCallback);
}
/*
* CAUTION: NLua.Lua instances can't share the same lua state!
*/
public Lua(LuaState luaState)
{
luaState.PushString("NLua_Loaded");
luaState.GetTable((int)LuaRegistry.Index);
if (luaState.ToBoolean(-1))
{
luaState.SetTop(-2);
throw new LuaException("There is already a NLua.Lua instance associated with this Lua state");
}
else
{
this.luaState = luaState;
_StatePassed = true;
luaState.SetTop(-2);
Init();
}
}
void Init()
{
luaState.PushString("NLua_Loaded");
luaState.PushBoolean(true);
luaState.SetTable((int)LuaRegistry.Index);
if (_StatePassed == false)
{
luaState.NewTable();
luaState.SetGlobal("luanet");
}
luaState.PushGlobalTable();
luaState.GetGlobal("luanet");
luaState.PushString("getmetatable");
luaState.GetGlobal("getmetatable");
luaState.SetTable(-3);
luaState.PopGlobalTable();
translator = new ObjectTranslator(this, luaState);
lock (translatorPoolLock)
{
ObjectTranslatorPool.Instance.Add(luaState, translator);
}
luaState.PopGlobalTable();
luaState.DoString(initLuanet);
}
public void Close()
{
if (_StatePassed)
return;
if (luaState != null)
{
lock (translatorPoolLock)
{
luaState.Close();
ObjectTranslatorPool.Instance.Remove(luaState);
luaState = null;
}
}
}
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int PanicCallback(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
string reason = string.Format("Unprotected error in call to Lua API ({0})", luaState.ToString(-1));
throw new LuaException(reason);
}
/// <summary>
/// Assuming we have a Lua error string sitting on the stack, throw a C# exception out to the user's app
/// </summary>
/// <exception cref = "LuaScriptException">Thrown if the script caused an exception</exception>
private void ThrowExceptionFromError(int oldTop)
{
object err = translator.GetObject(luaState, -1);
luaState.SetTop(oldTop);
// A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved)
var luaEx = err as LuaScriptException;
if (luaEx != null)
throw luaEx;
// A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it
if (err == null)
err = "Unknown Lua Error";
throw new LuaScriptException(err.ToString(), string.Empty);
}
/// <summary>
/// Push a debug.traceback reference onto the stack, for a pcall function to use as error handler. (Remember to increment any top-of-stack markers!)
/// </summary>
private static int PushDebugTraceback(LuaState luaState, int argCount)
{
luaState.GetGlobal("debug");
luaState.GetField(-1, "traceback");
luaState.Remove(-2);
int errindex = -argCount - 2;
luaState.Insert(errindex);
return errindex;
}
/// <summary>
/// <para>Return a debug.traceback() call result (a multi-line string, containing a full stack trace, including C calls.</para>
/// <para>Note: it won't return anything unless the interpreter is in the middle of execution - that is, it only makes sense to call it from a method called from Lua, or during a coroutine yield.</para>
/// </summary>
public string GetDebugTraceback()
{
int oldTop = luaState.GetTop();
luaState.GetGlobal("debug"); // stack: debug
luaState.GetField(-1, "traceback"); // stack: debug,traceback
luaState.Remove(-2); // stack: traceback
luaState.PCall(0, -1, 0);
return translator.PopValues(luaState, oldTop)[0] as string;
}
/// <summary>
/// Convert C# exceptions into Lua errors
/// </summary>
/// <returns>num of things on stack</returns>
/// <param name = "e">null for no pending exception</param>
internal int SetPendingException(Exception e)
{
var caughtExcept = e;
if (caughtExcept != null)
{
translator.ThrowError(luaState, caughtExcept);
luaState.PushNil();
return 1;
}
else
return 0;
}
/// <summary>
///
/// </summary>
/// <param name = "chunk"></param>
/// <param name = "name"></param>
/// <returns></returns>
public LuaFunction LoadString(string chunk, string name)
{
int oldTop = luaState.GetTop();
executing = true;
try
{
if (luaState.LoadString(chunk, name) != LuaStatus.OK)
ThrowExceptionFromError(oldTop);
}
finally
{
executing = false;
}
var result = translator.GetFunction(luaState, -1);
translator.PopValues(luaState, oldTop);
return result;
}
/// <summary>
///
/// </summary>
/// <param name = "chunk"></param>
/// <param name = "name"></param>
/// <returns></returns>
public LuaFunction LoadString(byte[] chunk, string name)
{
int oldTop = luaState.GetTop();
executing = true;
try
{
if (luaState.LoadBuffer(chunk, name) != LuaStatus.OK)
ThrowExceptionFromError(oldTop);
}
finally
{
executing = false;
}
var result = translator.GetFunction(luaState, -1);
translator.PopValues(luaState, oldTop);
return result;
}
/// <summary>
/// Load a File on, and return a LuaFunction to execute the file loaded (useful to see if the syntax of a file is ok)
/// </summary>
/// <param name = "fileName"></param>
/// <returns></returns>
public LuaFunction LoadFile(string fileName)
{
int oldTop = luaState.GetTop();
if (luaState.LoadFile(fileName) != LuaStatus.OK)
ThrowExceptionFromError(oldTop);
var result = translator.GetFunction(luaState, -1);
translator.PopValues(luaState, oldTop);
return result;
}
/// <summary>
/// Executes a Lua chunk and returns all the chunk's return values in an array.
/// </summary>
/// <param name = "chunk">Chunk to execute</param>
/// <param name = "chunkName">Name to associate with the chunk. Defaults to "chunk".</param>
/// <returns></returns>
public object[] DoString(byte[] chunk, string chunkName = "chunk")
{
int oldTop = luaState.GetTop();
executing = true;
if (luaState.LoadBuffer(chunk, chunkName) == LuaStatus.OK)
{
int errfunction = 0;
if (UseTraceback)
{
errfunction = PushDebugTraceback(luaState, 0);
oldTop++;
}
try
{
if (luaState.PCall(0, -1, errfunction) == LuaStatus.OK)
return translator.PopValues(luaState, oldTop);
else
ThrowExceptionFromError(oldTop);
}
finally
{
executing = false;
}
}
else
ThrowExceptionFromError(oldTop);
return null;
}
/// <summary>
/// Executes a Lua chunk and returns all the chunk's return values in an array.
/// </summary>
/// <param name = "chunk">Chunk to execute</param>
/// <param name = "chunkName">Name to associate with the chunk. Defaults to "chunk".</param>
/// <returns></returns>
public object[] DoString(string chunk, string chunkName = "chunk")
{
int oldTop = luaState.GetTop();
executing = true;
if (luaState.LoadString(chunk, chunkName) == LuaStatus.OK)
{
int errfunction = 0;
if (UseTraceback)
{
errfunction = PushDebugTraceback(luaState, 0);
oldTop++;
}
try
{
if (luaState.PCall(0, -1, errfunction) == LuaStatus.OK)
return translator.PopValues(luaState, oldTop);
else
ThrowExceptionFromError(oldTop);
}
finally
{
executing = false;
}
}
else
ThrowExceptionFromError(oldTop);
return null;
}
/*
* Excutes a Lua file and returns all the chunk's return
* values in an array
*/
public object[] DoFile(string fileName)
{
int oldTop = luaState.GetTop();
if (luaState.LoadFile(fileName) == LuaStatus.OK)
{
executing = true;
int errfunction = 0;
if (UseTraceback)
{
errfunction = PushDebugTraceback(luaState, 0);
oldTop++;
}
try
{
if (luaState.PCall(0, -1, errfunction) == LuaStatus.OK)
return translator.PopValues(luaState, oldTop);
else
ThrowExceptionFromError(oldTop);
}
finally
{
executing = false;
}
}
else
ThrowExceptionFromError(oldTop);
return null;
}
/*
* Indexer for global variables from the LuaInterpreter
* Supports navigation of tables by using . operator
*/
public object this[string fullPath] {
get
{
object returnValue = null;
int oldTop = luaState.GetTop();
string[] path = FullPathToArray(fullPath);
luaState.GetGlobal(path[0]);
returnValue = translator.GetObject(luaState, -1);
if (path.Length > 1)
{
var dispose = returnValue as LuaBase;
string[] remainingPath = new string[path.Length - 1];
Array.Copy(path, 1, remainingPath, 0, path.Length - 1);
returnValue = GetObject(remainingPath);
if (dispose != null)
dispose.Dispose();
}
luaState.SetTop(oldTop);
return returnValue;
}
set
{
int oldTop = luaState.GetTop();
string[] path = FullPathToArray(fullPath);
if (path.Length == 1)
{
translator.Push(luaState, value);
luaState.SetGlobal(fullPath);
}
else
{
luaState.GetGlobal(path[0]);
string[] remainingPath = new string[path.Length - 1];
Array.Copy(path, 1, remainingPath, 0, path.Length - 1);
SetObject(remainingPath, value);
}
luaState.SetTop(oldTop);
// Globals auto-complete
if (value == null)
{
// Remove now obsolete entries
globals.Remove(fullPath);
}
else
{
// Add new entries
if (!globals.Contains(fullPath))
RegisterGlobal(fullPath, value.GetType(), 0);
}
}
}
#region Globals auto-complete
/// <summary>
/// Adds an entry to <see cref = "globals"/> (recursivley handles 2 levels of members)
/// </summary>
/// <param name = "path">The index accessor path ot the entry</param>
/// <param name = "type">The type of the entry</param>
/// <param name = "recursionCounter">How deep have we gone with recursion?</param>
private void RegisterGlobal(string path, Type type, int recursionCounter)
{
// If the type is a global method, list it directly
if (type == typeof(LuaFunction))
{
// Format for easy method invocation
globals.Add(path + "(");
}
// If the type is a class or an interface and recursion hasn't been running too long, list the members
else if ((type.IsClass || type.IsInterface) && type != typeof(string) && recursionCounter < 2)
{
#region Methods
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))
{
string name = method.Name;
if (
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
(!method.GetCustomAttributes(typeof(LuaHideAttribute), false).Any()) &&
(!method.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Any()) &&
// Exclude some generic .NET methods that wouldn't be very usefull in Lua
name != "GetType" && name != "GetHashCode" && name != "Equals" &&
name != "ToString" && name != "Clone" && name != "Dispose" &&
name != "GetEnumerator" && name != "CopyTo" &&
!name.StartsWith("get_", StringComparison.Ordinal) &&
!name.StartsWith("set_", StringComparison.Ordinal) &&
!name.StartsWith("add_", StringComparison.Ordinal) &&
!name.StartsWith("remove_", StringComparison.Ordinal))
{
// Format for easy method invocation
string command = path + ":" + name + "(";
if (method.GetParameters().Length == 0)
command += ")";
globals.Add(command);
}
}
#endregion
#region Fields
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
{
if (
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
(!field.GetCustomAttributes(typeof(LuaHideAttribute), false).Any()) &&
(!field.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Any()))
{
// Go into recursion for members
RegisterGlobal(path + "." + field.Name, field.FieldType, recursionCounter + 1);
}
}
#endregion
#region Properties
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
(!property.GetCustomAttributes(typeof(LuaHideAttribute), false).Any()) &&
(!property.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Any())
// Exclude some generic .NET properties that wouldn't be very useful in Lua
&& property.Name != "Item")
{
// Go into recursion for members
RegisterGlobal(path + "." + property.Name, property.PropertyType, recursionCounter + 1);
}
}
#endregion
}
else
globals.Add(path); // Otherwise simply add the element to the list
// List will need to be sorted on next access
globalsSorted = false;
}
#endregion
/*
* Navigates a table in the top of the stack, returning
* the value of the specified field
*/
object GetObject(string[] remainingPath)
{
object returnValue = null;
for (int i = 0; i < remainingPath.Length; i++)
{
luaState.PushString(remainingPath[i]);
luaState.GetTable(-2);
returnValue = translator.GetObject(luaState, -1);
if (returnValue == null)
break;
}
return returnValue;
}
/*
* Gets a numeric global variable
*/
public double GetNumber(string fullPath)
{
return (double)this[fullPath];
}
/*
* Gets a string global variable
*/
public string GetString(string fullPath)
{
return this[fullPath].ToString();
}
/*
* Gets a table global variable
*/
public LuaTable GetTable(string fullPath)
{
return (LuaTable)this[fullPath];
}
/*
* Gets a table global variable as an object implementing
* the interfaceType interface
*/
public object GetTable(Type interfaceType, string fullPath)
{
return CodeGeneration.Instance.GetClassInstance(interfaceType, GetTable(fullPath));
}
/*
* Gets a function global variable
*/
public LuaFunction GetFunction(string fullPath)
{
object obj = this[fullPath];
LuaFunction luaFunction = obj as LuaFunction;
if (luaFunction != null)
return luaFunction;
luaFunction = new LuaFunction((LuaNativeFunction) obj, this);
return luaFunction;
}
/*
* Register a delegate type to be used to convert Lua functions to C# delegates (useful for iOS where there is no dynamic code generation)
* type delegateType
*/
public void RegisterLuaDelegateType(Type delegateType, Type luaDelegateType)
{
CodeGeneration.Instance.RegisterLuaDelegateType(delegateType, luaDelegateType);
}
public void RegisterLuaClassType(Type klass, Type luaClass)
{
CodeGeneration.Instance.RegisterLuaClassType(klass, luaClass);
}
public void LoadCLRPackage()
{
luaState.DoString(Lua.clr_package);
}
/*
* Gets a function global variable as a delegate of
* type delegateType
*/
public Delegate GetFunction(Type delegateType, string fullPath)
{
return CodeGeneration.Instance.GetDelegate(delegateType, GetFunction(fullPath));
}
/*
* Calls the object as a function with the provided arguments,
* returning the function's returned values inside an array
*/
internal object[] CallFunction(object function, object[] args)
{
return CallFunction(function, args, null);
}
/*
* Calls the object as a function with the provided arguments and
* casting returned values to the types in returnTypes before returning
* them in an array
*/
internal object[] CallFunction(object function, object[] args, Type[] returnTypes)
{
int nArgs = 0;
int oldTop = luaState.GetTop();
if (!luaState.CheckStack(args.Length + 6))
throw new LuaException("Lua stack overflow");
translator.Push(luaState, function);
if (args.Length > 0)
{
nArgs = args.Length;
for (int i = 0; i < args.Length; i++)
translator.Push(luaState, args[i]);
}
executing = true;
try
{
int errfunction = 0;
if (UseTraceback)
{
errfunction = PushDebugTraceback(luaState, nArgs);
oldTop++;
}
LuaStatus error = luaState.PCall(nArgs, -1, errfunction);
if (error != LuaStatus.OK)
ThrowExceptionFromError(oldTop);
}
finally
{
executing = false;
}
if (returnTypes != null)
return translator.PopValues(luaState, oldTop, returnTypes);
return translator.PopValues(luaState, oldTop);
}
/*
* Navigates a table to set the value of one of its fields
*/
void SetObject(string[] remainingPath, object val)
{
for (int i = 0; i < remainingPath.Length - 1; i++)
{
luaState.PushString(remainingPath[i]);
luaState.GetTable(-2);
}
luaState.PushString(remainingPath[remainingPath.Length - 1]);
translator.Push(luaState, val);
luaState.SetTable(-3);
}
string[] FullPathToArray(string fullPath)
{
return fullPath.SplitWithEscape('.', '\\').ToArray();
}
/*
* Creates a new table as a global variable or as a field
* inside an existing table
*/
public void NewTable(string fullPath)
{
string[] path = FullPathToArray(fullPath);
int oldTop = luaState.GetTop();
if (path.Length == 1)
{
luaState.NewTable();
luaState.SetGlobal(fullPath);
}
else
{
luaState.GetGlobal(path[0]);
for (int i = 1; i < path.Length - 1; i++)
{
luaState.PushString(path[i]);
luaState.GetTable(-2);
}
luaState.PushString(path[path.Length - 1]);
luaState.NewTable();
luaState.SetTable(-3);
}
luaState.SetTop( oldTop);
}
public Dictionary<object, object> GetTableDict(LuaTable table)
{
var dict = new Dictionary<object, object>();
int oldTop = luaState.GetTop();
translator.Push(luaState, table);
luaState.PushNil();
while (luaState.Next(-2))
{
dict[translator.GetObject(luaState, -2)] = translator.GetObject(luaState, -1);
luaState.SetTop(-2);
}
luaState.SetTop(oldTop);
return dict;
}
/*
* Lets go of a previously allocated reference to a table, function
* or userdata
*/
#region lua debug functions
/// <summary>
/// Activates the debug hook
/// </summary>
/// <param name = "mask">Mask</param>
/// <param name = "count">Count</param>
/// <returns>see lua docs. -1 if hook is already set</returns>
public int SetDebugHook(LuaHookMask mask, int count)
{
if (hookCallback == null)
{
hookCallback = new LuaHookFunction(Lua.DebugHookCallback);
luaState.SetHook(hookCallback, mask, count);
}
return -1;
}
/// <summary>
/// Removes the debug hook
/// </summary>
/// <returns>see lua docs</returns>
public void RemoveDebugHook()
{
hookCallback = null;
luaState.SetHook(null, LuaHookMask.Disabled, 0);
}
/// <summary>
/// Gets the hook mask.
/// </summary>
/// <returns>hook mask</returns>
public LuaHookMask GetHookMask()
{
return luaState.HookMask;
}
/// <summary>
/// Gets the hook count
/// </summary>
/// <returns>see lua docs</returns>
public int GetHookCount()
{
return luaState.HookCount;
}
/// <summary>
/// Gets local (see lua docs)
/// </summary>
/// <param name = "luaDebug">lua debug structure</param>
/// <param name = "n">see lua docs</param>
/// <returns>see lua docs</returns>
public string GetLocal(LuaDebug luaDebug, int n)
{
return luaState.GetLocal(luaDebug, n);
}
/// <summary>
/// Sets local (see lua docs)
/// </summary>
/// <param name = "luaDebug">lua debug structure</param>
/// <param name = "n">see lua docs</param>
/// <returns>see lua docs</returns>
public string SetLocal(LuaDebug luaDebug, int n)
{
return luaState.SetLocal(luaDebug, n);
}
public int GetStack(int level, ref LuaDebug ar)
{
return luaState.GetStack(level, ref ar);
}
public bool GetInfo(string what, ref LuaDebug ar)
{
return luaState.GetInfo(what, ref ar);
}
/// <summary>
/// Gets up value (see lua docs)
/// </summary>
/// <param name = "funcindex">see lua docs</param>
/// <param name = "n">see lua docs</param>
/// <returns>see lua docs</returns>
public string GetUpValue(int funcindex, int n)
{
return luaState.GetUpValue(funcindex, n);
}
/// <summary>
/// Sets up value (see lua docs)
/// </summary>
/// <param name = "funcindex">see lua docs</param>
/// <param name = "n">see lua docs</param>
/// <returns>see lua docs</returns>
public string SetUpValue(int funcindex, int n)
{
return luaState.SetUpValue(funcindex, n);
}
/// <summary>
/// Delegate that is called on lua hook callback
/// </summary>
/// <param name = "luaState">lua state</param>
/// <param name = "luaDebug">Pointer to LuaDebug (lua_debug) structure</param>
///
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaHookFunction))]
#endif
static void DebugHookCallback(IntPtr luaState, IntPtr luaDebug)
{
var state = LuaState.FromIntPtr(luaState);
state.GetStack(0, luaDebug);
if (!state.GetInfo("Snlu", luaDebug))
return;
var debug = LuaDebug.FromIntPtr(luaDebug);
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(state);
Lua lua = translator.Interpreter;
lua.DebugHookCallbackInternal(state, debug);
}
private void DebugHookCallbackInternal(LuaState luaState, LuaDebug luaDebug)
{
try
{
var temp = DebugHook;
if (temp != null)
temp(this, new DebugHookEventArgs(luaDebug));
}
catch (Exception ex)
{
OnHookException(new HookExceptionEventArgs(ex));
}
}
private void OnHookException(HookExceptionEventArgs e)
{
var temp = HookException;
if (temp != null)
temp(this, e);
}
/// <summary>
/// Pops a value from the lua stack.
/// </summary>
/// <returns>Returns the top value from the lua stack.</returns>
public object Pop()
{
int top = luaState.GetTop();
return translator.PopValues(luaState, top - 1)[0];
}
/// <summary>
/// Pushes a value onto the lua stack.
/// </summary>
/// <param name = "value">Value to push.</param>
public void Push(object value)
{
translator.Push(luaState, value);
}
#endregion
internal void DisposeInternal(int reference)
{
if (luaState != null)
luaState.Unref(reference);
}
/*
* Gets a field of the table corresponding to the provided reference
* using rawget (do not use metatables)
*/
internal object RawGetObject(int reference, string field)
{
int oldTop = luaState.GetTop();
luaState.GetRef(reference);
luaState.PushString(field);
luaState.RawGet(-2);
object obj = translator.GetObject(luaState, -1);
luaState.SetTop(oldTop);
return obj;
}
/*
* Gets a field of the table or userdata corresponding to the provided reference
*/
internal object GetObject(int reference, string field)
{
int oldTop = luaState.GetTop();
luaState.GetRef(reference);
object returnValue = GetObject(FullPathToArray(field));
luaState.SetTop(oldTop);
return returnValue;
}
/*
* Gets a numeric field of the table or userdata corresponding the the provided reference
*/
internal object GetObject(int reference, object field)
{
int oldTop = luaState.GetTop();
luaState.GetRef(reference);
translator.Push(luaState, field);
luaState.GetTable(-2);
object returnValue = translator.GetObject(luaState, -1);
luaState.SetTop(oldTop);
return returnValue;
}
/*
* Sets a field of the table or userdata corresponding the the provided reference
* to the provided value
*/
internal void SetObject(int reference, string field, object val)
{
int oldTop = luaState.GetTop();
luaState.GetRef(reference);
SetObject(FullPathToArray(field), val);
luaState.SetTop(oldTop);
}
/*
* Sets a numeric field of the table or userdata corresponding the the provided reference
* to the provided value
*/
internal void SetObject(int reference, object field, object val)
{
int oldTop = luaState.GetTop();
luaState.GetRef(reference);
translator.Push(luaState, field);
translator.Push(luaState, val);
luaState.SetTable(-3);
luaState.SetTop(oldTop);
}
public LuaFunction RegisterFunction(string path, MethodBase function /*MethodInfo function*/)
{
return RegisterFunction(path, null, function);
}
/*
* 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)
{
// We leave nothing on the stack when we are done
int oldTop = luaState.GetTop();
var wrapper = new LuaMethodWrapper(translator, target, new ProxyType(function.DeclaringType), function);
translator.Push(luaState, new LuaNativeFunction(wrapper.invokeFunction));
this[path] = translator.GetObject(luaState, -1);
var f = GetFunction(path);
luaState.SetTop(oldTop);
return f;
}
/*
* Compares the two values referenced by ref1 and ref2 for equality
*/
internal bool CompareRef(int ref1, int ref2)
{
int top = luaState.GetTop();
luaState.GetRef(ref1);
luaState.GetRef(ref2);
bool equal = luaState.AreEqual(-1, -2);
luaState.SetTop(top);
return equal;
}
internal void PushCSFunction(LuaNativeFunction function)
{
translator.PushFunction(luaState, function);
}
#region IDisposable Members
public virtual void Dispose()
{
if (translator != null)
{
translator.pendingEvents.Dispose();
translator = null;
}
Close();
GC.WaitForPendingFinalizers();
}
#endregion
}
}
using System;
namespace NLua
{
/// <summary>
/// Base class to provide consistent disposal flow across lua objects. Uses code provided by Yves Duhoux and suggestions by Hans Schmeidenbacher and Qingrui Li
/// </summary>
public abstract class LuaBase : IDisposable
{
private bool _Disposed;
protected int
_Reference;
protected Lua
_Interpreter;
~LuaBase()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void Dispose(bool disposeManagedResources)
{
if (!_Disposed)
{
if (disposeManagedResources)
{
if (_Reference != 0)
_Interpreter.DisposeInternal(_Reference);
}
_Interpreter = null;
_Disposed = true;
}
}
public override bool Equals(object o)
{
if (o is LuaBase)
{
var l = (LuaBase)o;
return _Interpreter.CompareRef(l._Reference, _Reference);
}
else
return false;
}
public override int GetHashCode()
{
return _Reference;
}
}
}
\ No newline at end of file
using System;
using KeraLua;
using LuaState = KeraLua.Lua;
using LuaNativeFunction = KeraLua.LuaFunction;
namespace NLua
{
public class LuaFunction : LuaBase
{
internal LuaNativeFunction function;
public LuaFunction(int reference, Lua interpreter)
{
_Reference = reference;
function = null;
_Interpreter = interpreter;
}
public LuaFunction(LuaNativeFunction nativeFunction, Lua interpreter)
{
_Reference = 0;
function = nativeFunction;
_Interpreter = interpreter;
}
/*
* Calls the function casting return values to the types
* in returnTypes
*/
internal object[] Call(object[] args, Type[] returnTypes)
{
return _Interpreter.CallFunction(this, args, returnTypes);
}
/*
* Calls the function and returns its return values inside
* an array
*/
public object[] Call(params object[] args)
{
return _Interpreter.CallFunction(this, args);
}
/*
* Pushes the function into the Lua stack
*/
internal void Push(LuaState luaState)
{
if (_Reference != 0)
luaState.RawGetInteger(LuaRegistry.Index, _Reference);
else
_Interpreter.PushCSFunction(function);
}
public override string ToString()
{
return "function";
}
public override bool Equals(object o)
{
if (o is LuaFunction)
{
var l = (LuaFunction)o;
if (this._Reference != 0 && l._Reference != 0)
return _Interpreter.CompareRef(l._Reference, this._Reference);
else
return this.function == l.function;
}
else
return false;
}
public override int GetHashCode()
{
return _Reference != 0 ? _Reference : function.GetHashCode();
}
}
}
\ No newline at end of file
using System;
namespace NLua
{
/// <summary>
/// Marks a method for global usage in Lua scripts
/// </summary>
/// <see cref="LuaRegistrationHelper.TaggedInstanceMethods"/>
/// <see cref="LuaRegistrationHelper.TaggedStaticMethods"/>
[AttributeUsage(AttributeTargets.Method)]
public sealed class LuaGlobalAttribute : Attribute
{
/// <summary>
/// An alternative name to use for calling the function in Lua - leave empty for CLR name
/// </summary>
public string Name { get; set; }
/// <summary>
/// A description of the function
/// </summary>
public string Description { get; set; }
}
}
\ No newline at end of file
using System;
namespace NLua
{
/// <summary>
/// Marks a method, field or property to be hidden from Lua auto-completion
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
public sealed class LuaHideAttribute : Attribute
{
}
}
\ No newline at end of file
using System;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;
using NLua.Extensions;
namespace NLua
{
public static class LuaRegistrationHelper
{
#region Tagged instance methods
/// <summary>
/// Registers all public instance methods in an object tagged with <see cref="LuaGlobalAttribute"/> as Lua global functions
/// </summary>
/// <param name="lua">The Lua VM to add the methods to</param>
/// <param name="o">The object to get the methods from</param>
public static void TaggedInstanceMethods(Lua lua, object o)
{
#region Sanity checks
if (lua == null)
throw new ArgumentNullException("lua");
if (o == null)
throw new ArgumentNullException("o");
#endregion
foreach (var method in o.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public))
{
foreach (LuaGlobalAttribute attribute in method.GetCustomAttributes(typeof(LuaGlobalAttribute), true))
{
if (string.IsNullOrEmpty(attribute.Name))
lua.RegisterFunction(method.Name, o, method); // CLR name
else
lua.RegisterFunction(attribute.Name, o, method); // Custom name
}
}
}
#endregion
#region Tagged static methods
/// <summary>
/// Registers all public static methods in a class tagged with <see cref="LuaGlobalAttribute"/> as Lua global functions
/// </summary>
/// <param name="lua">The Lua VM to add the methods to</param>
/// <param name="type">The class type to get the methods from</param>
public static void TaggedStaticMethods(Lua lua, Type type)
{
#region Sanity checks
if (lua == null)
throw new ArgumentNullException("lua");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsClass)
throw new ArgumentException("The type must be a class!", "type");
#endregion
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
{
foreach (LuaGlobalAttribute attribute in method.GetCustomAttributes(typeof(LuaGlobalAttribute), false))
{
if (string.IsNullOrEmpty(attribute.Name))
lua.RegisterFunction(method.Name, null, method); // CLR name
else
lua.RegisterFunction(attribute.Name, null, method); // Custom name
}
}
}
#endregion
/// <summary>
/// Registers an enumeration's values for usage as a Lua variable table
/// </summary>
/// <typeparam name="T">The enum type to register</typeparam>
/// <param name="lua">The Lua VM to add the enum to</param>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "The type parameter is used to select an enum type")]
public static void Enumeration<T>(Lua lua)
{
if (lua == null)
throw new ArgumentNullException("lua");
var type = typeof(T);
if (!type.IsEnum)
throw new ArgumentException("The type must be an enumeration!");
string[] names = Enum.GetNames(type);
var values = (T[])Enum.GetValues(type);
lua.NewTable(type.Name);
for (int i = 0; i < names.Length; i++)
{
string path = type.Name + "." + names[i];
lua[path] = values[i];
}
}
}
}
\ No newline at end of file

using System.Collections;
using NLua.Extensions;
using LuaState = KeraLua.Lua;
namespace NLua
{
public class LuaTable : LuaBase
{
public LuaTable(int reference, Lua interpreter)
{
_Reference = reference;
_Interpreter = interpreter;
}
/*
* Indexer for string fields of the table
*/
public object this[string field] {
get
{
return _Interpreter.GetObject(_Reference, field);
}
set
{
_Interpreter.SetObject(_Reference, field, value);
}
}
/*
* Indexer for numeric fields of the table
*/
public object this[object field] {
get
{
return _Interpreter.GetObject(_Reference, field);
}
set
{
_Interpreter.SetObject(_Reference, field, value);
}
}
public IDictionaryEnumerator GetEnumerator()
{
return _Interpreter.GetTableDict(this).GetEnumerator();
}
public ICollection Keys {
get { return _Interpreter.GetTableDict(this).Keys; }
}
public ICollection Values {
get { return _Interpreter.GetTableDict(this).Values; }
}
/*
* Gets an string fields of a table ignoring its metatable,
* if it exists
*/
internal object RawGet(string field)
{
return _Interpreter.RawGetObject(_Reference, field);
}
/*
* Pushes this table into the Lua stack
*/
internal void Push(LuaState luaState)
{
luaState.GetRef(_Reference);
}
public override string ToString()
{
return "table";
}
}
}
\ No newline at end of file

namespace NLua
{
public class LuaUserData : LuaBase
{
public LuaUserData(int reference, Lua interpreter)
{
_Reference = reference;
_Interpreter = interpreter;
}
/*
* Indexer for string fields of the userdata
*/
public object this[string field] {
get
{
return _Interpreter.GetObject(_Reference, field);
}
set
{
_Interpreter.SetObject(_Reference, field, value);
}
}
/*
* Indexer for numeric fields of the userdata
*/
public object this[object field] {
get
{
return _Interpreter.GetObject(_Reference, field);
}
set
{
_Interpreter.SetObject(_Reference, field, value);
}
}
/*
* Calls the userdata and returns its return values inside
* an array
*/
public object[] Call(params object[] args)
{
return _Interpreter.CallFunction(this, args);
}
public override string ToString()
{
return "userdata";
}
}
}
\ No newline at end of file
using System;
using System.Linq;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using KeraLua;
using NLua.Method;
using NLua.Extensions;
#if __IOS__ || __TVOS__ || __WATCHOS__
using ObjCRuntime;
#endif
using LuaState = KeraLua.Lua;
using LuaNativeFunction = KeraLua.LuaFunction;
namespace NLua
{
public class MetaFunctions
{
public LuaNativeFunction GcFunction { get; }
public LuaNativeFunction IndexFunction { get; }
public LuaNativeFunction NewIndexFunction { get; }
public LuaNativeFunction BaseIndexFunction { get; }
public LuaNativeFunction ClassIndexFunction { get; }
public LuaNativeFunction ClassNewIndexFunction { get; }
public LuaNativeFunction ExecuteDelegateFunction { get; }
public LuaNativeFunction CallConstructorFunction { get; }
public LuaNativeFunction ToStringFunction { get; }
public LuaNativeFunction CallDelegateFunction { get; }
public LuaNativeFunction AddFunction { get; }
public LuaNativeFunction SubtractFunction { get; }
public LuaNativeFunction MultiplyFunction { get; }
public LuaNativeFunction DivisionFunction { get; }
public LuaNativeFunction ModulosFunction { get; }
public LuaNativeFunction UnaryNegationFunction { get; }
public LuaNativeFunction EqualFunction { get; }
public LuaNativeFunction LessThanFunction { get; }
public LuaNativeFunction LessThanOrEqualFunction { get; }
Dictionary<object, object> memberCache = new Dictionary<object, object>();
ObjectTranslator translator;
/*
* __index metafunction for CLR objects. Implemented in Lua.
*/
static 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 static string LuaIndexFunction => luaIndexFunction;
public MetaFunctions(ObjectTranslator translator)
{
this.translator = translator;
GcFunction = CollectObject;
ToStringFunction = ToStringLua;
IndexFunction = GetMethod;
NewIndexFunction = SetFieldOrProperty;
BaseIndexFunction = GetBaseMethod;
CallConstructorFunction =CallConstructor;
ClassIndexFunction = GetClassMethod;
ClassNewIndexFunction = SetClassFieldOrProperty;
ExecuteDelegateFunction = RunFunctionDelegate;
CallDelegateFunction = CallDelegate;
AddFunction = AddLua;
SubtractFunction = SubtractLua;
MultiplyFunction = MultiplyLua;
DivisionFunction = DivideLua;
ModulosFunction = ModLua;
UnaryNegationFunction = UnaryNegationLua;
EqualFunction = EqualLua;
LessThanFunction = LessThanLua;
LessThanOrEqualFunction = LessThanOrEqualLua;
}
/*
* __call metafunction of CLR delegates, retrieves and calls the delegate.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int RunFunctionDelegate(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
var func = (LuaNativeFunction)translator.GetRawNetObject(state, 1);
state.Remove(1);
return func(luaState);
}
/*
* __gc metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int CollectObject(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
return CollectObject(luaState, translator);
}
private static int CollectObject(LuaState luaState, ObjectTranslator translator)
{
int udata = luaState.RawNetObj(1);
if (udata != -1)
translator.CollectObject(udata);
return 0;
}
/*
* __tostring metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int ToStringLua(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
return ToStringLua(luaState, translator);
}
private static int ToStringLua(LuaState luaState, ObjectTranslator translator)
{
object obj = translator.GetRawNetObject(luaState, 1);
if (obj != null)
translator.Push(luaState, obj + ": " + obj.GetHashCode());
else
luaState.PushNil();
return 1;
}
/*
* __add metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int AddLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_Addition", translator);
}
/*
* __sub metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int SubtractLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_Subtraction", translator);
}
/*
* __mul metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int MultiplyLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_Multiply", translator);
}
/*
* __div metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int DivideLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_Division", translator);
}
/*
* __mod metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int ModLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_Modulus", translator);
}
/*
* __unm metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int UnaryNegationLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return UnaryNegationLua(state, translator);
}
static int UnaryNegationLua(LuaState luaState, ObjectTranslator translator)
{
object obj1 = translator.GetRawNetObject(luaState, 1);
if (obj1 == null)
{
translator.ThrowError(luaState, "Cannot negate a nil object");
luaState.PushNil();
return 1;
}
Type type = obj1.GetType();
MethodInfo opUnaryNegation = type.GetMethod("op_UnaryNegation");
if (opUnaryNegation == null)
{
translator.ThrowError(luaState, "Cannot negate object (" + type.Name + " does not overload the operator -)");
luaState.PushNil();
return 1;
}
obj1 = opUnaryNegation.Invoke(obj1, new [] { obj1 });
translator.Push(luaState, obj1);
return 1;
}
/*
* __eq metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int EqualLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_Equality", translator);
}
/*
* __lt metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int LessThanLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_LessThan", translator);
}
/*
* __le metafunction of CLR objects.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int LessThanOrEqualLua(IntPtr luaState)
{
var state = LuaState.FromIntPtr(luaState);
var translator = ObjectTranslatorPool.Instance.Find(state);
return MatchOperator(state, "op_LessThanOrEqual", translator);
}
/// <summary>
/// Debug tool to dump the lua stack
/// </summary>
/// FIXME, move somewhere else
public static void DumpStack(ObjectTranslator translator, LuaState luaState)
{
int depth = luaState.GetTop();
Debug.WriteLine("lua stack depth: {0}", depth);
for (int i = 1; i <= depth; i++)
{
var type = luaState.Type(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 == LuaType.Table) ? "table" : luaState.TypeName(type);
string strrep = luaState.ToString(i);
if (type == LuaType.UserData)
{
object obj = translator.GetRawNetObject(luaState, i);
strrep = obj.ToString();
}
Debug.WriteLine("{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.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int GetMethod(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
var instance = translator.MetaFunctionsInstance;
return instance.GetMethodInternal(luaState);
}
private int GetMethodInternal(LuaState luaState)
{
object obj = translator.GetRawNetObject(luaState, 1);
if (obj == null)
{
translator.ThrowError(luaState, "Trying to index an invalid object reference");
luaState.PushNil();
return 1;
}
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);
// 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);
}
// 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);
Type type = objType.UnderlyingSystemType;
if (type == typeof(float[]))
{
float[] arr = (float[])obj;
translator.Push(luaState, arr[intIndex]);
}
else if (type == typeof(double[]))
{
double[] arr = (double[])obj;
translator.Push(luaState, arr[intIndex]);
}
else if (type == typeof(int[]))
{
int[] arr = (int[])obj;
translator.Push(luaState, arr[intIndex]);
}
else
{
object[] arr = (object[])obj;
translator.Push(luaState, arr[intIndex]);
}
}
else
{
if (!string.IsNullOrEmpty(methodName) && IsExtensionMethodPresent(objType, methodName))
{
return GetExtensionMethod(luaState, objType, obj, methodName);
}
// Try to use get_Item to index into this .net object
var methods = objType.GetMethods();
foreach (var methodInfo in methods)
{
if (methodInfo.Name == "get_Item")
{
// Check if the signature matches the input
if (methodInfo.GetParameters().Length == 1)
{
var actualParams = methodInfo.GetParameters();
if (actualParams.Length != 1)
{
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);
object[] args = new object[1];
// Just call the indexer - if out of bounds an exception will happen
args[0] = index;
try
{
object result = methodInfo.Invoke(obj, args);
translator.Push(luaState, result);
}
catch (TargetInvocationException e)
{
// Provide a more readable description for the common case of key not found
if (e.InnerException is KeyNotFoundException)
translator.ThrowError(luaState, "key '" + index + "' not found ");
else
translator.ThrowError(luaState, "exception indexing '" + index + "' " + e.Message);
luaState.PushNil();
}
}
}
}
}
}
luaState.PushBoolean(false);
return 2;
}
/*
* __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.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int GetBaseMethod(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
var instance = translator.MetaFunctionsInstance;
return instance.GetBaseMethodInternal(luaState);
}
private int GetBaseMethodInternal(LuaState luaState)
{
object obj = translator.GetRawNetObject(luaState, 1);
if (obj == null)
{
translator.ThrowError(luaState, "Trying to index an invalid object reference");
luaState.PushNil();
luaState.PushBoolean(false);
return 2;
}
string methodName = luaState.ToString(2);
if (string.IsNullOrEmpty(methodName))
{
luaState.PushNil();
luaState.PushBoolean(false);
return 2;
}
GetMember(luaState, new ProxyType(obj.GetType()), obj, "__luaInterface_base_" + methodName, BindingFlags.Instance);
luaState.SetTop(-2);
if (luaState.Type(-1) == LuaType.Nil)
{
luaState.SetTop(-2);
return GetMember(luaState, new ProxyType(obj.GetType()), obj, methodName, BindingFlags.Instance);
}
luaState.PushBoolean(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(ProxyType objType, string methodName)
{
object cachedMember = CheckMemberCache(objType, methodName);
if (cachedMember != null)
return true;
var members = objType.GetMember(methodName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
return members.Length > 0;
}
bool IsExtensionMethodPresent(Type type, string name)
{
object cachedMember = CheckMemberCache(type, name);
if (cachedMember != null)
return true;
return translator.IsExtensionMethodPresent(type, name);
}
int GetExtensionMethod(LuaState luaState, Type type, object obj, string name)
{
var cachedMember = CheckMemberCache(type, name) as LuaNativeFunction;
if (cachedMember != null)
{
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 invokeDelegate = new LuaNativeFunction(methodWrapper.invokeFunction);
SetMemberCache(type, name, invokeDelegate);
translator.PushFunction(luaState, invokeDelegate);
translator.Push(luaState, true);
return 2;
}
/*
* 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).
*/
int GetMember(LuaState luaState, ProxyType objType, object obj, string methodName, BindingFlags bindingType)
{
bool implicitStatic = false;
MemberInfo member = null;
object cachedMember = CheckMemberCache(objType, methodName);
if (cachedMember is LuaNativeFunction)
{
translator.PushFunction(luaState, (LuaNativeFunction)cachedMember);
translator.Push(luaState, true);
return 2;
}
if (cachedMember != null)
member = (MemberInfo)cachedMember;
else
{
var members = objType.GetMember(methodName, bindingType | BindingFlags.Public);
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
members = objType.GetMember(methodName, bindingType | BindingFlags.Static | BindingFlags.Public);
if (members.Length > 0)
{
member = members[0];
implicitStatic = true;
}
}
}
if (member != null)
{
if (member.MemberType == MemberTypes.Field)
{
var field = (FieldInfo)member;
if (cachedMember == null)
SetMemberCache(objType, methodName, member);
try
{
var value = field.GetValue(obj);
translator.Push(luaState, value);
}
catch
{
Debug.WriteLine("[Exception] Fail to get field value");
luaState.PushNil();
}
}
else if (member.MemberType == MemberTypes.Property)
{
var property = (PropertyInfo)member;
if (cachedMember == null)
SetMemberCache(objType, methodName, member);
try
{
object value = property.GetValue(obj, null);
translator.Push(luaState, value);
}
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.UnderlyingSystemType != typeof(object))
return GetMember(luaState, new ProxyType(objType.UnderlyingSystemType.BaseType), obj, methodName, bindingType);
luaState.PushNil();
}
catch (TargetInvocationException e)
{ // Convert this exception into a Lua error
ThrowError(luaState, e);
luaState.PushNil();
}
}
else if (member.MemberType == MemberTypes.Event)
{
var eventInfo = (EventInfo)member;
if (cachedMember == null)
SetMemberCache(objType, methodName, member);
translator.Push(luaState, new RegisterEventHandler(translator.pendingEvents, obj, eventInfo));
}
else if (!implicitStatic)
{
if (member.MemberType == MemberTypes.NestedType)
{
if (cachedMember == null)
SetMemberCache(objType, methodName, member);
// Find the name of our class
string name = member.Name;
var 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);
}
else
{
// Member type must be 'method'
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);
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);
luaState.PushNil();
}
}
else
{
if (objType.UnderlyingSystemType != typeof(object))
return GetMember(luaState, new ProxyType(objType.UnderlyingSystemType.BaseType), obj, methodName, bindingType);
// 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);
luaState.PushNil();
}
// 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.
*/
object CheckMemberCache(Type objType, string memberName)
{
return CheckMemberCache(new ProxyType(objType), memberName);
}
object CheckMemberCache(ProxyType objType, string memberName)
{
object members = null;
if (memberCache.TryGetValue(objType, out members))
{
var membersDict = members as Dictionary<object, object>;
object memberValue = null;
if (members != null && membersDict.TryGetValue(memberName, out memberValue))
{
return memberValue;
}
}
return null;
}
/*
* Stores a MemberInfo object in the member cache.
*/
void SetMemberCache(Type objType, string memberName, object member)
{
SetMemberCache(new ProxyType(objType), memberName, member);
}
void SetMemberCache(ProxyType objType, string memberName, object member)
{
Dictionary<object, object> members = null;
object memberCacheValue = null;
if (memberCache.TryGetValue(objType, out memberCacheValue))
{
members = (Dictionary<object, object>)memberCacheValue;
}
else
{
members = new Dictionary<object, object>();
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.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int SetFieldOrProperty(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
var instance = translator.MetaFunctionsInstance;
return instance.SetFieldOrPropertyInternal(luaState);
}
private int SetFieldOrPropertyInternal(LuaState luaState)
{
object target = translator.GetRawNetObject(luaState, 1);
if (target == null)
{
translator.ThrowError(luaState, "trying to index and invalid object reference");
return 0;
}
var type = target.GetType();
// First try to look up the parameter as a property name
string detailMessage;
bool didMember = TrySetMember(luaState, new ProxyType(type), target, BindingFlags.Instance, 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 && luaState.IsNumber(2))
{
int index = (int)luaState.ToNumber(2);
var 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
var setter = type.GetMethod("set_Item");
if (setter != null)
{
var args = setter.GetParameters();
var valueType = args[1].ParameterType;
// The new val ue the user specified
object val = translator.GetAsType(luaState, 3, valueType);
var 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
}
#if !SILVERLIGHT
}
catch (SEHException)
{
// If we are seeing a C++ exception - this must actually be for Lua's private use. Let it handle it
throw;
#endif
}
catch (Exception e)
{
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>
bool TrySetMember(LuaState luaState, ProxyType 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 (luaState.Type(2) != LuaType.String)
{
detailMessage = "property names must be strings";
return false;
}
// We only look up property names by string
string fieldName = luaState.ToString(2);
if (string.IsNullOrEmpty(fieldName) || !(char.IsLetter(fieldName[0]) || fieldName[0] == '_'))
{
detailMessage = "Invalid property name";
return false;
}
// Find our member via reflection or the cache
var member = (MemberInfo)CheckMemberCache(targetType, fieldName);
if (member == null)
{
var members = targetType.GetMember(fieldName, bindingType | BindingFlags.Public);
if (members.Length > 0)
{
member = members[0];
SetMemberCache(targetType, fieldName, member);
}
else
{
detailMessage = "field or property '" + fieldName + "' does not exist";
return false;
}
}
if (member.MemberType == MemberTypes.Field)
{
var field = (FieldInfo)member;
object val = translator.GetAsType(luaState, 3, field.FieldType);
try
{
field.SetValue(target, val);
}
catch (Exception e)
{
ThrowError(luaState, e);
}
return true;
}
if (member.MemberType == MemberTypes.Property)
{
var property = (PropertyInfo)member;
object val = translator.GetAsType(luaState, 3, property.PropertyType);
try
{
property.SetValue(target, val, null);
}
catch (Exception e)
{
ThrowError(luaState, e);
}
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(LuaState luaState, ProxyType 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(LuaState luaState, Exception e)
{
// If we got inside a reflection show what really happened
var te = e as TargetInvocationException;
if (te != null)
e = te.InnerException;
translator.ThrowError(luaState, e);
}
/*
* __index metafunction of type references, works on static members.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int GetClassMethod(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
var instance = translator.MetaFunctionsInstance;
return instance.GetClassMethodInternal(luaState);
}
private int GetClassMethodInternal(LuaState luaState)
{
ProxyType klass;
object obj = translator.GetRawNetObject(luaState, 1);
if (obj == null || !(obj is ProxyType))
{
translator.ThrowError(luaState, "Trying to index an invalid type reference");
luaState.PushNil();
return 1;
}
klass = (ProxyType)obj;
if (luaState.IsNumber(2))
{
int size = (int)luaState.ToNumber(2);
translator.Push(luaState, Array.CreateInstance(klass.UnderlyingSystemType, size));
return 1;
}
string methodName = luaState.ToString(2);
if (string.IsNullOrEmpty(methodName))
{
luaState.PushNil();
return 1;
}
return GetMember(luaState, klass, null, methodName, BindingFlags.Static);
}
/*
* __newindex function of type references, works on static members.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int SetClassFieldOrProperty(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
var instance = translator.MetaFunctionsInstance;
return instance.SetClassFieldOrPropertyInternal(luaState);
}
private int SetClassFieldOrPropertyInternal(LuaState luaState)
{
ProxyType target;
object obj = translator.GetRawNetObject(luaState, 1);
if (obj == null || !(obj is ProxyType))
{
translator.ThrowError(luaState, "trying to index an invalid type reference");
return 0;
}
target = (ProxyType)obj;
return SetMember(luaState, target, null, BindingFlags.Static);
}
/*
* __call metafunction of Delegates.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
static int CallDelegate(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
var instance = translator.MetaFunctionsInstance;
return instance.CallDelegateInternal(luaState);
}
int CallDelegateInternal(LuaState luaState)
{
object objDelegate = translator.GetRawNetObject(luaState, 1);
if (objDelegate == null || !(objDelegate is Delegate))
{
translator.ThrowError(luaState, "Trying to invoke a not delegate or callable value");
luaState.PushNil();
return 1;
}
luaState.Remove(1);
var validDelegate = new MethodCache();
var del = (Delegate)objDelegate;
MethodBase methodDelegate = del.Method;
bool isOk = MatchParameters(luaState, methodDelegate, ref validDelegate);
if (isOk)
{
object result;
if (methodDelegate.IsStatic)
result = methodDelegate.Invoke(null, validDelegate.args);
else
result = methodDelegate.Invoke(del.Target, validDelegate.args);
translator.Push(luaState, result);
return 1;
}
translator.ThrowError(luaState, "Cannot invoke delegate (invalid arguments for " + methodDelegate.Name + ")");
luaState.PushNil();
return 1;
}
/*
* __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.
*/
#if __IOS__ || __TVOS__ || __WATCHOS__
[MonoPInvokeCallback(typeof(LuaNativeFunction))]
#endif
private static int CallConstructor(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var translator = ObjectTranslatorPool.Instance.Find(luaState);
var instance = translator.MetaFunctionsInstance;
return instance.CallConstructorInternal(luaState);
}
private int CallConstructorInternal(LuaState luaState)
{
var validConstructor = new MethodCache();
ProxyType klass;
object obj = translator.GetRawNetObject(luaState, 1);
if (obj == null || !(obj is ProxyType))
{
translator.ThrowError(luaState, "Trying to call constructor on an invalid type reference");
luaState.PushNil();
return 1;
}
klass = (ProxyType)obj;
luaState.Remove(1);
ConstructorInfo[] constructors = klass.UnderlyingSystemType.GetConstructors();
foreach (var constructor in constructors)
{
bool isConstructor = MatchParameters(luaState, constructor, ref validConstructor);
if (isConstructor)
{
try
{
translator.Push(luaState, constructor.Invoke(validConstructor.args));
}
catch (TargetInvocationException e)
{
ThrowError(luaState, e);
luaState.PushNil();
}
catch
{
luaState.PushNil();
}
return 1;
}
}
if (klass.UnderlyingSystemType.IsValueType)
{
int numLuaParams = luaState.GetTop();
if (numLuaParams == 0)
{
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",
klass.UnderlyingSystemType, constructorName));
luaState.PushNil();
return 1;
}
static bool IsInteger(double x)
{
return Math.Ceiling(x) == x;
}
static object GetTargetObject(LuaState luaState, string operation, ObjectTranslator translator)
{
Type t;
object target = translator.GetRawNetObject(luaState, 1);
if (target != null)
{
t = target.GetType();
if (t.HasMethod(operation))
return target;
}
target = translator.GetRawNetObject(luaState, 2);
if (target != null)
{
t = target.GetType();
if (t.HasMethod(operation))
return target;
}
return null;
}
static int MatchOperator(LuaState luaState, string operation, ObjectTranslator translator)
{
var validOperator = new MethodCache();
object target = GetTargetObject(luaState, operation, translator);
if (target == null)
{
translator.ThrowError(luaState, "Cannot call " + operation + " on a nil object");
luaState.PushNil();
return 1;
}
Type type = target.GetType();
var operators = type.GetMethods(operation, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (var op in operators)
{
bool isOk = translator.MatchParameters(luaState, op, ref validOperator);
if (!isOk)
continue;
object result;
if (op.IsStatic)
result = op.Invoke(null, validOperator.args);
else
result = op.Invoke(target, validOperator.args);
translator.Push(luaState, result);
return 1;
}
translator.ThrowError(luaState, "Cannot call (" + operation + ") on object type " + type.Name);
luaState.PushNil();
return 1;
}
internal Array TableToArray(Func<int, object> luaParamValueExtractor, Type paramArrayType, int startIndex, int count)
{
Array paramArray;
if (count == 0)
return Array.CreateInstance(paramArrayType, 0);
var luaParamValue = luaParamValueExtractor(startIndex);
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 value = tableEnumerator.Value;
if (paramArrayType == typeof(object))
{
if (value != null && value.GetType() == typeof(double) && IsInteger((double)value))
value = Convert.ToInt32((double)value);
}
#if SILVERLIGHT
paramArray.SetValue (Convert.ChangeType (value, paramArrayType, System.Globalization.CultureInfo.InvariantCulture), paramArrayIndex);
#else
paramArray.SetValue(Convert.ChangeType(value, paramArrayType), paramArrayIndex);
#endif
paramArrayIndex++;
}
}
else
{
paramArray = Array.CreateInstance(paramArrayType, count);
paramArray.SetValue(luaParamValue, 0);
for (int i = 1; i < count; i++)
{
startIndex++;
var value = luaParamValueExtractor(startIndex);
paramArray.SetValue(value, i);
}
}
return paramArray;
}
/*
* Matches a method against its arguments in the Lua stack. Returns
* 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)
{
ExtractValue extractValue;
bool isMethod = true;
var paramInfo = method.GetParameters();
int currentLuaParam = 1;
int nLuaParams = luaState.GetTop();
var paramList = new List<object>();
var outList = new List<int>();
var argTypes = new List<MethodArgs>();
foreach (var currentNetParam in paramInfo)
{
if (!currentNetParam.IsIn && currentNetParam.IsOut) // Skips out params
{
paramList.Add(null);
outList.Add(paramList.LastIndexOf(null));
} // Type does not match, ignore if the parameter is optional
else if (IsParamsArray(luaState, nLuaParams, currentLuaParam, currentNetParam, out extractValue))
{
int count = (nLuaParams - currentLuaParam) + 1;
Type paramArrayType = currentNetParam.ParameterType.GetElementType();
Func<int, object> extractDelegate = (currentParam) => {
currentLuaParam++;
return extractValue(luaState, currentParam);
};
Array paramArray = TableToArray(extractDelegate, paramArrayType, currentLuaParam, count);
paramList.Add(paramArray);
int index = paramList.LastIndexOf(paramArray);
var methodArg = new MethodArgs();
methodArg.index = index;
methodArg.extractValue = extractValue;
methodArg.isParamsArray = true;
methodArg.paramsArrayType = paramArrayType;
argTypes.Add(methodArg);
}
else if (IsTypeCorrect(luaState, currentLuaParam, currentNetParam, out extractValue))
{ // Type checking
var value = extractValue(luaState, currentLuaParam);
paramList.Add(value);
int index = paramList.LastIndexOf(value);
var methodArg = new MethodArgs();
methodArg.index = index;
methodArg.extractValue = extractValue;
argTypes.Add(methodArg);
if (currentNetParam.ParameterType.IsByRef)
outList.Add(index);
currentLuaParam++;
}
else if (currentLuaParam > nLuaParams)
{ // Adds optional parameters
if (currentNetParam.IsOptional)
paramList.Add(currentNetParam.DefaultValue);
else
{
isMethod = false;
break;
}
}
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>
/// 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(LuaState luaState, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue)
{
try
{
return (extractValue = translator.typeChecker.CheckLuaType(luaState, currentLuaParam, currentNetParam.ParameterType)) != null;
}
catch
{
extractValue = null;
Debug.WriteLine("Type wasn't correct");
return false;
}
}
private bool IsParamsArray(LuaState luaState, int nLuaParams, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue)
{
extractValue = null;
bool isParamArray = false;
if (currentNetParam.GetCustomAttributes(typeof(ParamArrayAttribute), false).Any())
{
isParamArray = nLuaParams < currentLuaParam;
LuaType luaType;
try
{
luaType = luaState.Type(currentLuaParam);
}
catch (Exception ex)
{
Debug.WriteLine("Could not retrieve lua type while attempting to determine params Array Status.");
Debug.WriteLine(ex.Message);
extractValue = null;
return false;
}
if (luaType == LuaType.Table)
{
try
{
extractValue = translator.typeChecker.GetExtractor(typeof(LuaTable));
}
catch (Exception)
{
Debug.WriteLine("An error occurred during an attempt to retrieve a LuaTable extractor while checking for params array status.");
}
if (extractValue != null)
{
return true;
}
}
else
{
var paramElementType = currentNetParam.ParameterType.GetElementType();
try
{
extractValue = translator.typeChecker.CheckLuaType(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.", paramElementType.FullName));
}
if (extractValue != null)
{
return true;
}
}
}
return isParamArray;
}
}
}
\ No newline at end of file
using System;
using System.Diagnostics;
using System.Collections.Generic;
namespace NLua.Method
{
/// <summary>
/// We keep track of what delegates we have auto attached to an event - to allow us to cleanly exit a NLua session
/// </summary>
class EventHandlerContainer : IDisposable
{
private Dictionary<Delegate, RegisterEventHandler> dict = new Dictionary<Delegate, RegisterEventHandler>();
public void Add(Delegate handler, RegisterEventHandler eventInfo)
{
dict.Add(handler, eventInfo);
}
public void Remove(Delegate handler)
{
bool found = dict.Remove(handler);
Debug.Assert(found);
}
/// <summary>
/// Remove any still registered handlers
/// </summary>
public void Dispose()
{
foreach (KeyValuePair<Delegate, RegisterEventHandler> pair in dict)
pair.Value.RemovePending(pair.Key);
dict.Clear();
}
}
}
\ No newline at end of file
using System;
namespace NLua.Method
{
/*
* Static helper methods for Lua tables acting as CLR objects.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
public class LuaClassHelper
{
/*
* Gets the function called name from the provided table,
* returning null if it does not exist
*/
public static LuaFunction GetTableFunction(LuaTable luaTable, string name)
{
if (luaTable == null)
return null;
object funcObj = luaTable.RawGet(name);
if (funcObj is LuaFunction)
return (LuaFunction)funcObj;
else
return null;
}
/*
* Calls the provided function with the provided parameters
*/
public static object CallFunction(LuaFunction function, object[] args, Type[] returnTypes, object[] inArgs, int[] outArgs)
{
// args is the return array of arguments, inArgs is the actual array
// of arguments passed to the function (with in parameters only), outArgs
// has the positions of out parameters
object returnValue;
int iRefArgs;
object[] returnValues = function.Call(inArgs, returnTypes);
if (returnTypes[0] == typeof(void))
{
returnValue = null;
iRefArgs = 0;
}
else
{
returnValue = returnValues[0];
iRefArgs = 1;
}
for (int i = 0; i < outArgs.Length; i++)
{
args[outArgs[i]] = returnValues[iRefArgs];
iRefArgs++;
}
return returnValue;
}
}
}
\ No newline at end of file
using System;
namespace NLua.Method
{
public class LuaDelegate
{
public LuaFunction function;
public Type[] returnTypes;
public LuaDelegate()
{
function = null;
returnTypes = null;
}
public object CallFunction(object[] args, object[] inArgs, int[] outArgs)
{
// args is the return array of arguments, inArgs is the actual array
// of arguments passed to the function (with in parameters only), outArgs
// has the positions of out parameters
object returnValue;
int iRefArgs;
object[] returnValues = function.Call(inArgs, returnTypes);
if (returnTypes[0] == typeof(void))
{
returnValue = null;
iRefArgs = 0;
}
else
{
returnValue = returnValues[0];
iRefArgs = 1;
}
// Sets the value of out and ref parameters (from
// the values returned by the Lua function).
for (int i = 0; i < outArgs.Length; i++)
{
args[outArgs[i]] = returnValues[iRefArgs];
iRefArgs++;
}
return returnValue;
}
}
}
\ No newline at end of file
namespace NLua.Method
{
public class LuaEventHandler
{
public LuaFunction handler = null;
// CP: Fix provided by Ben Bryant for delegates with one param
// link: http://luaforge.net/forum/message.php?msg_id=9318
public void HandleEvent(object[] args)
{
handler.Call(args);
}
}
}
\ No newline at end of file
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using NLua.Exceptions;
using NLua.Extensions;
using LuaState = KeraLua.Lua;
using LuaNativeFunction = KeraLua.LuaFunction;
namespace NLua.Method
{
/*
* Argument extraction with type-conversion function
*/
delegate object ExtractValue(LuaState luaState, int stackPos);
/*
* Wrapper class for methods/constructors accessed from Lua.
*
*/
class LuaMethodWrapper
{
internal LuaNativeFunction invokeFunction;
ObjectTranslator _Translator;
MethodBase _Method;
MethodCache _LastCalledMethod = new MethodCache();
string _MethodName;
MemberInfo[] _Members;
ExtractValue _ExtractTarget;
object _Target;
bool _IsStatic;
/*
* Constructs the wrapper for a known MethodBase instance
*/
public LuaMethodWrapper(ObjectTranslator translator, object target, ProxyType targetType, MethodBase method)
{
invokeFunction = Call;
_Translator = translator;
_Target = target;
if (targetType != null)
_ExtractTarget = translator.typeChecker.GetExtractor(targetType);
_Method = method;
_MethodName = method.Name;
_IsStatic = method.IsStatic;
}
/*
* Constructs the wrapper for a known method name
*/
public LuaMethodWrapper(ObjectTranslator translator, ProxyType targetType, string methodName, BindingFlags bindingType)
{
invokeFunction = Call;
_Translator = translator;
_MethodName = methodName;
if (targetType != null)
_ExtractTarget = translator.typeChecker.GetExtractor(targetType);
_IsStatic = (bindingType & BindingFlags.Static) == BindingFlags.Static;
_Members = GetMethodsRecursively(targetType.UnderlyingSystemType, methodName, bindingType | BindingFlags.Public);
}
MethodInfo[] GetMethodsRecursively(Type type, string methodName, BindingFlags bindingType)
{
if (type == typeof(object))
return type.GetMethods(methodName, bindingType);
var methods = type.GetMethods(methodName, bindingType);
var baseMethods = GetMethodsRecursively(type.BaseType, methodName, bindingType);
return methods.Concat(baseMethods).ToArray();
}
/// <summary>
/// Convert C# exceptions into Lua errors
/// </summary>
/// <returns>num of things on stack</returns>
/// <param name="e">null for no pending exception</param>
int SetPendingException(Exception e)
{
return _Translator.interpreter.SetPendingException(e);
}
/*
* Calls the method. Receives the arguments from the Lua stack
* and returns values in it.
*/
int Call(IntPtr state)
{
var luaState = LuaState.FromIntPtr(state);
var 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];
int index = i + 1 + numStackToSkip;
Func<int, object> valueExtractor = currentParam => {
return type.extractValue(luaState, currentParam);
};
if (_LastCalledMethod.argTypes[i].isParamsArray)
{
int count = _LastCalledMethod.argTypes.Length - i;
Array paramArray = _Translator.TableToArray(valueExtractor, type.paramsArrayType, index, count);
args[_LastCalledMethod.argTypes[i].index] = paramArray;
}
else
{
args[type.index] = valueExtractor(index);
}
if (_LastCalledMethod.args[_LastCalledMethod.argTypes[i].index] == null &&
!luaState.IsNil(i + 1 + numStackToSkip))
throw new LuaException(string.Format("Argument number {0} is invalid", (i + 1)));
}
if (_IsStatic)
_Translator.Push(luaState, method.Invoke(null, _LastCalledMethod.args));
else
{
if (method.IsConstructor)
_Translator.Push(luaState, ((ConstructorInfo)method).Invoke(_LastCalledMethod.args));
else
_Translator.Push(luaState, method.Invoke(targetObject, _LastCalledMethod.args));
}
failedCall = false;
}
catch (TargetInvocationException e)
{
// Failure of method invocation
if (_Translator.interpreter.UseTraceback) e.GetBaseException().Data["Traceback"] = _Translator.interpreter.GetDebugTraceback();
return SetPendingException(e.GetBaseException());
}
catch (Exception e)
{
if (_Members.Length == 1) // Is the method overloaded?
// No, throw error
return SetPendingException(e);
}
}
}
// Cache miss
if (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 == null)
{
_Translator.ThrowError(luaState, string.Format("instance method '{0}' requires a non null target object", _MethodName));
luaState.PushNil();
return 1;
}
luaState.Remove(1); // Pops the receiver
}
bool hasMatch = false;
string candidateName = null;
foreach (var member in _Members)
{
candidateName = member.ReflectedType.Name + "." + member.Name;
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);
luaState.PushNil();
return 1;
}
}
}
else
{ // Method from MethodBase instance
if (methodToCall.ContainsGenericParameters)
{
_Translator.MatchParameters(luaState, methodToCall, ref _LastCalledMethod);
if (methodToCall.IsGenericMethodDefinition)
{
//need to make a concrete type of the generic method definition
var typeArgs = new List<Type>();
foreach (object arg in _LastCalledMethod.args)
typeArgs.Add(arg.GetType());
var concreteMethod = ((MethodInfo)methodToCall).MakeGenericMethod(typeArgs.ToArray());
_Translator.Push(luaState, concreteMethod.Invoke(targetObject, _LastCalledMethod.args));
failedCall = false;
}
else if (methodToCall.ContainsGenericParameters)
{
_Translator.ThrowError(luaState, "Unable to invoke method on generic class as the current method is an open generic method");
luaState.PushNil();
return 1;
}
}
else
{
if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null)
{
targetObject = _ExtractTarget(luaState, 1);
luaState.Remove(1); // Pops the receiver
}
if (!_Translator.MatchParameters(luaState, methodToCall, ref _LastCalledMethod))
{
_Translator.ThrowError(luaState, "Invalid arguments to method call");
luaState.PushNil();
return 1;
}
}
}
if (failedCall)
{
if (!luaState.CheckStack(_LastCalledMethod.outList.Length + 6))
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)
{
return SetPendingException(e);
}
}
// 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;
}
}
}
\ No newline at end of file
using System;
namespace NLua.Method
{
/*
* Parameter information
*/
struct MethodArgs
{
// Position of parameter
public int index;
// Type-conversion function
public ExtractValue extractValue;
public bool isParamsArray;
public Type paramsArrayType;
}
}
\ No newline at end of file
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