Add LuaInterface project

This commit is contained in:
ScottNZ
2013-11-15 21:55:38 +13:00
parent 55423b159a
commit 0a50371a37
21 changed files with 5425 additions and 0 deletions

View File

@@ -69,6 +69,9 @@ Also thanks to:
Using GeoLite data created by MaxMind and
distributed under the CC BY-SA 3.0 license.
Using MonoBoxedLua created by Sparklin Labs
and distributed under the MIT license.
Finally, special thanks goes to the original teams
at Westwood Studios and EA for creating the classic
games which OpenRA aims to reimagine.

View File

@@ -0,0 +1,56 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly: AssemblyTitle("LuaInterface")]
[assembly: AssemblyDescription("Bridge between the Lua runtime and the CLR")]
[assembly: AssemblyCopyright("Copyright 2003-2008 Fabio Mascarenhas, Kevin Hester")]
[assembly: CLSCompliant(false)]
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("2.0.4.*")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
// We call native DLLs (lua50) and we don't want to be fucked with or validated
// [assembly: SecurityPermission(RequestMinimum, UnmanagedCode = true)]

314
LuaInterface/CheckType.cs Normal file
View File

@@ -0,0 +1,314 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace LuaInterface
{
/*
* Type checking and conversion functions.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class CheckType
{
private ObjectTranslator translator;
ExtractValue extractNetObject;
Dictionary<long, ExtractValue> extractValues = new Dictionary<long, ExtractValue>();
public CheckType(ObjectTranslator translator)
{
this.translator = translator;
extractValues.Add(typeof(object).TypeHandle.Value.ToInt64(), new ExtractValue(getAsObject));
extractValues.Add(typeof(sbyte).TypeHandle.Value.ToInt64(), new ExtractValue(getAsSbyte));
extractValues.Add(typeof(byte).TypeHandle.Value.ToInt64(), new ExtractValue(getAsByte));
extractValues.Add(typeof(short).TypeHandle.Value.ToInt64(), new ExtractValue(getAsShort));
extractValues.Add(typeof(ushort).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUshort));
extractValues.Add(typeof(int).TypeHandle.Value.ToInt64(), new ExtractValue(getAsInt));
extractValues.Add(typeof(uint).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUint));
extractValues.Add(typeof(long).TypeHandle.Value.ToInt64(), new ExtractValue(getAsLong));
extractValues.Add(typeof(ulong).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUlong));
extractValues.Add(typeof(double).TypeHandle.Value.ToInt64(), new ExtractValue(getAsDouble));
extractValues.Add(typeof(char).TypeHandle.Value.ToInt64(), new ExtractValue(getAsChar));
extractValues.Add(typeof(float).TypeHandle.Value.ToInt64(), new ExtractValue(getAsFloat));
extractValues.Add(typeof(decimal).TypeHandle.Value.ToInt64(), new ExtractValue(getAsDecimal));
extractValues.Add(typeof(bool).TypeHandle.Value.ToInt64(), new ExtractValue(getAsBoolean));
extractValues.Add(typeof(string).TypeHandle.Value.ToInt64(), new ExtractValue(getAsString));
extractValues.Add(typeof(LuaFunction).TypeHandle.Value.ToInt64(), new ExtractValue(getAsFunction));
extractValues.Add(typeof(LuaTable).TypeHandle.Value.ToInt64(), new ExtractValue(getAsTable));
extractValues.Add(typeof(LuaUserData).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUserdata));
extractNetObject = new ExtractValue(getAsNetObject);
}
/*
* Checks if the value at Lua stack index stackPos matches paramType,
* returning a conversion function if it does and null otherwise.
*/
internal ExtractValue getExtractor(IReflect paramType)
{
return getExtractor(paramType.UnderlyingSystemType);
}
internal ExtractValue getExtractor(Type paramType)
{
if(paramType.IsByRef) paramType=paramType.GetElementType();
long runtimeHandleValue = paramType.TypeHandle.Value.ToInt64();
if(extractValues.ContainsKey(runtimeHandleValue))
return extractValues[runtimeHandleValue];
else
return extractNetObject;
}
internal ExtractValue checkType(IntPtr luaState,int stackPos,Type paramType)
{
LuaTypes luatype = LuaDLL.lua_type(luaState, stackPos);
if(paramType.IsByRef) paramType=paramType.GetElementType();
Type underlyingType = Nullable.GetUnderlyingType(paramType);
if (underlyingType != null)
{
paramType = underlyingType; // Silently convert nullable types to their non null requics
}
long runtimeHandleValue = paramType.TypeHandle.Value.ToInt64();
if (paramType.Equals(typeof(object)))
return extractValues[runtimeHandleValue];
/*
//CP: Added support for generic parameters
if (paramType.IsGenericParameter)
{
if (luatype == LuaTypes.LUA_TBOOLEAN)
return extractValues[typeof(bool).TypeHandle.Value.ToInt64()];
else if (luatype == LuaTypes.LUA_TSTRING)
return extractValues[typeof(string).TypeHandle.Value.ToInt64()];
else if (luatype == LuaTypes.LUA_TTABLE)
return extractValues[typeof(LuaTable).TypeHandle.Value.ToInt64()];
else if (luatype == LuaTypes.LUA_TUSERDATA)
return extractValues[typeof(object).TypeHandle.Value.ToInt64()];
else if (luatype == LuaTypes.LUA_TFUNCTION)
return extractValues[typeof(LuaFunction).TypeHandle.Value.ToInt64()];
else if (luatype == LuaTypes.LUA_TNUMBER)
return extractValues[typeof(double).TypeHandle.Value.ToInt64()];
//else // suppress CS0642
;//an unsupported type was encountered
}
*/
if (LuaDLL.lua_isnumber(luaState, stackPos))
return extractValues[runtimeHandleValue];
if (paramType == typeof(bool))
{
if (LuaDLL.lua_isboolean(luaState, stackPos))
return extractValues[runtimeHandleValue];
}
else if (paramType == typeof(string))
{
if (LuaDLL.lua_isstring(luaState, stackPos))
return extractValues[runtimeHandleValue];
else if (luatype == LuaTypes.LUA_TNIL)
return extractNetObject; // kevinh - silently convert nil to a null string pointer
}
else if (paramType == typeof(LuaTable))
{
if (luatype == LuaTypes.LUA_TTABLE)
return extractValues[runtimeHandleValue];
else if (luatype == LuaTypes.LUA_TNIL)
return extractNetObject; // tkopal - silently convert nil to a null table
}
else if (paramType == typeof(LuaUserData))
{
if (luatype == LuaTypes.LUA_TUSERDATA)
return extractValues[runtimeHandleValue];
}
else if (paramType == typeof(LuaFunction))
{
if (luatype == LuaTypes.LUA_TFUNCTION)
return extractValues[runtimeHandleValue];
else if (luatype == LuaTypes.LUA_TNIL)
return extractNetObject; // elisee - silently convert nil to a null string pointer
}
else if (typeof(Delegate).IsAssignableFrom(paramType) && luatype == LuaTypes.LUA_TFUNCTION)
{
return new ExtractValue(new DelegateGenerator(translator, paramType).extractGenerated);
}
else if (paramType.IsInterface && luatype == LuaTypes.LUA_TTABLE)
{
return new ExtractValue(new ClassGenerator(translator, paramType).extractGenerated);
}
else if ((paramType.IsInterface || paramType.IsClass) && luatype == LuaTypes.LUA_TNIL)
{
// kevinh - allow nil to be silently converted to null - extractNetObject will return null when the item ain't found
return extractNetObject;
}
else if (LuaDLL.lua_type(luaState, stackPos) == LuaTypes.LUA_TTABLE)
{
if (LuaDLL.luaL_getmetafield(luaState, stackPos, "__index"))
{
object obj = translator.getNetObject(luaState, -1);
LuaDLL.lua_settop(luaState, -2);
if (obj != null && paramType.IsAssignableFrom(obj.GetType()))
return extractNetObject;
}
else
return null;
}
else
{
object obj = translator.getNetObject(luaState, stackPos);
if (obj != null && paramType.IsAssignableFrom(obj.GetType()))
return extractNetObject;
}
return null;
}
/*
* The following functions return the value in the Lua stack
* index stackPos as the desired type if it can, or null
* otherwise.
*/
private object getAsSbyte(IntPtr luaState,int stackPos)
{
sbyte retVal=(sbyte)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsByte(IntPtr luaState,int stackPos)
{
byte retVal=(byte)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsShort(IntPtr luaState,int stackPos)
{
short retVal=(short)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsUshort(IntPtr luaState,int stackPos)
{
ushort retVal=(ushort)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsInt(IntPtr luaState,int stackPos)
{
int retVal=(int)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsUint(IntPtr luaState,int stackPos)
{
uint retVal=(uint)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsLong(IntPtr luaState,int stackPos)
{
long retVal=(long)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsUlong(IntPtr luaState,int stackPos)
{
ulong retVal=(ulong)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsDouble(IntPtr luaState,int stackPos)
{
double retVal=LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsChar(IntPtr luaState,int stackPos)
{
char retVal=(char)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsFloat(IntPtr luaState,int stackPos)
{
float retVal=(float)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsDecimal(IntPtr luaState,int stackPos)
{
decimal retVal=(decimal)LuaDLL.lua_tonumber(luaState,stackPos);
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
return retVal;
}
private object getAsBoolean(IntPtr luaState,int stackPos)
{
return LuaDLL.lua_toboolean(luaState,stackPos);
}
private object getAsString(IntPtr luaState,int stackPos)
{
string retVal=LuaDLL.lua_tostring(luaState,stackPos);
if(retVal=="" && !LuaDLL.lua_isstring(luaState,stackPos)) return null;
return retVal;
}
private object getAsTable(IntPtr luaState,int stackPos)
{
return translator.getTable(luaState,stackPos);
}
private object getAsFunction(IntPtr luaState,int stackPos)
{
return translator.getFunction(luaState,stackPos);
}
private object getAsUserdata(IntPtr luaState,int stackPos)
{
return translator.getUserData(luaState,stackPos);
}
public object getAsObject(IntPtr luaState,int stackPos)
{
if(LuaDLL.lua_type(luaState,stackPos)==LuaTypes.LUA_TTABLE)
{
if(LuaDLL.luaL_getmetafield(luaState,stackPos,"__index"))
{
if(LuaDLL.luaL_checkmetatable(luaState,-1))
{
LuaDLL.lua_insert(luaState,stackPos);
LuaDLL.lua_remove(luaState,stackPos+1);
}
else
{
LuaDLL.lua_settop(luaState,-2);
}
}
}
object obj=translator.getObject(luaState,stackPos);
return obj;
}
public object getAsNetObject(IntPtr luaState,int stackPos)
{
object obj=translator.getNetObject(luaState,stackPos);
if(obj==null && LuaDLL.lua_type(luaState,stackPos)==LuaTypes.LUA_TTABLE)
{
if(LuaDLL.luaL_getmetafield(luaState,stackPos,"__index"))
{
if(LuaDLL.luaL_checkmetatable(luaState,-1))
{
LuaDLL.lua_insert(luaState,stackPos);
LuaDLL.lua_remove(luaState,stackPos+1);
obj=translator.getNetObject(luaState,stackPos);
}
else
{
LuaDLL.lua_settop(luaState,-2);
}
}
}
return obj;
}
}
}

View File

@@ -0,0 +1,683 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace LuaInterface
{
/*
* 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;
}
/*
* 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 __luaInterface_getLuaTable();
}
/*
* Class used for generating delegates that get a function from the Lua
* stack as a delegate of a specific type.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class DelegateGenerator
{
private ObjectTranslator translator;
private Type delegateType;
public DelegateGenerator(ObjectTranslator translator,Type delegateType)
{
this.translator=translator;
this.delegateType=delegateType;
}
public object extractGenerated(IntPtr luaState,int stackPos)
{
return CodeGeneration.Instance.GetDelegate(delegateType,translator.getFunction(luaState,stackPos));
}
}
/*
* Class used for generating delegates that get a table from the Lua
* stack as a an object of a specific type.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class ClassGenerator
{
private ObjectTranslator translator;
private Type klass;
public ClassGenerator(ObjectTranslator translator,Type klass)
{
this.translator=translator;
this.klass=klass;
}
public object extractGenerated(IntPtr luaState,int stackPos)
{
return CodeGeneration.Instance.GetClassInstance(klass,translator.getTable(luaState,stackPos));
}
}
/*
* Dynamically generates new types from existing types and
* Lua function and table values. Generated types are event handlers,
* delegates, interface implementations and subclasses.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class CodeGeneration
{
private Type eventHandlerParent=typeof(LuaEventHandler);
private Dictionary<Type, Type> eventHandlerCollection=new Dictionary<Type, Type>();
private Type delegateParent=typeof(LuaDelegate);
private Dictionary<Type, Type> delegateCollection=new Dictionary<Type, Type>();
private Type classHelper=typeof(LuaClassHelper);
private Dictionary<Type, LuaClassType> classCollection=new Dictionary<Type, LuaClassType>();
private AssemblyName assemblyName;
private AssemblyBuilder newAssembly;
private ModuleBuilder newModule;
private int luaClassNumber=1;
private static readonly CodeGeneration instance = new CodeGeneration();
static CodeGeneration()
{
}
private CodeGeneration()
{
// Create an assembly name
assemblyName=new AssemblyName( );
assemblyName.Name="LuaInterface_generatedcode";
// Create a new assembly with one module.
newAssembly=Thread.GetDomain().DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.Run);
newModule=newAssembly.DefineDynamicModule("LuaInterface_generatedcode");
}
/*
* 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)
{
string typeName;
lock(this)
{
typeName = "LuaGeneratedClass" + luaClassNumber;
luaClassNumber++;
}
// Define a public class in the assembly, called typeName
TypeBuilder myType=newModule.DefineType(typeName,TypeAttributes.Public,eventHandlerParent);
// Defines the handler method. Its signature is void(object,<subclassofEventArgs>)
Type[] paramTypes = new Type[2];
paramTypes[0]=typeof(object);
paramTypes[1]=eventHandlerType;
Type returnType=typeof(void);
MethodBuilder 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);
MethodInfo miGenericEventHandler;
miGenericEventHandler=eventHandlerParent.GetMethod("handleEvent");
generator.Emit(OpCodes.Call,miGenericEventHandler);
// returns
generator.Emit(OpCodes.Ret);
// creates the new type
return myType.CreateType();
}
/*
* Generates a type that can be used for instantiating a delegate
* of the provided type, given a Lua function.
*/
private Type GenerateDelegate(Type delegateType)
{
string typeName;
lock(this)
{
typeName = "LuaGeneratedClass" + luaClassNumber;
luaClassNumber++;
}
// Define a public class in the assembly, called typeName
TypeBuilder myType=newModule.DefineType(typeName,TypeAttributes.Public,delegateParent);
// Defines the delegate method with the same signature as the
// Invoke method of delegateType
MethodInfo invokeMethod=delegateType.GetMethod("Invoke");
ParameterInfo[] paramInfo=invokeMethod.GetParameters();
Type[] paramTypes=new Type[paramInfo.Length];
Type 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];
MethodBuilder 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);
MethodInfo miGenericEventHandler;
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);
// creates the new type
return myType.CreateType();
}
/*
* 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, LuaTable luaTable)
{
string typeName;
lock(this)
{
typeName = "LuaGeneratedClass" + luaClassNumber;
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
FieldBuilder luaTableField=myType.DefineField("__luaInterface_luaTable",typeof(LuaTable),FieldAttributes.Public);
// Field that stores the return types array
FieldBuilder 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
ConstructorBuilder 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 and protected virtual methods that have been explicitly specfied
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
MethodInfo[] classMethods=klass.GetMethods(flags);
returnTypes=new Type[classMethods.Length][];
int i=0;
foreach(MethodInfo 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)
{
if (luaTable[method.Name] != null) {
GenerateMethod(myType,method,(method.Attributes|MethodAttributes.NewSlot)^MethodAttributes.NewSlot,i,
luaTableField,returnTypesField,true,out returnTypes[i]);
i++;
}
}
}
}
// Generates an implementation of the __luaInterface_getLuaTable method
MethodBuilder returnTableMethod=myType.DefineMethod("__luaInterface_getLuaTable",
MethodAttributes.Public|MethodAttributes.HideBySig|MethodAttributes.Virtual,
typeof(LuaTable),new Type[0]);
myType.DefineMethodOverride(returnTableMethod,typeof(ILuaGeneratedType).GetMethod("__luaInterface_getLuaTable"));
generator=returnTableMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld,luaTableField);
generator.Emit(OpCodes.Ret);
// Creates the type
newType=myType.CreateType();
}
/*
* 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)
{
ParameterInfo[] paramInfo=method.GetParameters();
Type[] paramTypes=new Type[paramInfo.Length];
List<Type> 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;
Type 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)
{
String baseName = "__luaInterface_base_"+method.Name;
MethodBuilder baseMethod=myType.DefineMethod(baseName,
MethodAttributes.Public|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
MethodBuilder 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"));
Label 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);
Label 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);
}
/*
* 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)
{
Type eventConsumerType;
if (eventHandlerCollection.ContainsKey(eventHandlerType))
{
eventConsumerType=eventHandlerCollection[eventHandlerType];
}
else
{
eventConsumerType=GenerateEvent(eventHandlerType);
eventHandlerCollection[eventHandlerType] = eventConsumerType;
}
LuaEventHandler luaEventHandler=(LuaEventHandler)Activator.CreateInstance(eventConsumerType);
luaEventHandler.handler=eventHandler;
return luaEventHandler;
}
/*
* Gets a delegate with delegateType that calls the luaFunc Lua function
* Caches the generated type.
*/
public Delegate GetDelegate(Type delegateType, LuaFunction luaFunc)
{
List<Type> returnTypes=new List<Type>();
Type luaDelegateType;
if (delegateCollection.ContainsKey(delegateType))
{
luaDelegateType=delegateCollection[delegateType];
}
else
{
luaDelegateType=GenerateDelegate(delegateType);
delegateCollection[delegateType] = luaDelegateType;
}
MethodInfo methodInfo=delegateType.GetMethod("Invoke");
returnTypes.Add(methodInfo.ReturnType);
foreach(ParameterInfo paramInfo in methodInfo.GetParameters())
if(paramInfo.ParameterType.IsByRef)
returnTypes.Add(paramInfo.ParameterType);
LuaDelegate luaDelegate=(LuaDelegate)Activator.CreateInstance(luaDelegateType);
luaDelegate.function=luaFunc;
luaDelegate.returnTypes=returnTypes.ToArray();
return Delegate.CreateDelegate(delegateType,luaDelegate,"CallFunction");
}
/*
* 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,luaTable);
classCollection[klass] = luaClassType;
}
return Activator.CreateInstance(luaClassType.klass,new object[] {luaTable,luaClassType.returnTypes});
}
}
}

761
LuaInterface/Lua.cs Normal file
View File

@@ -0,0 +1,761 @@
namespace LuaInterface
{
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.Threading;
using OpenRA;
using System.Text;
/*
* Main class of LuaInterface
* Object-oriented wrapper to Lua API
*
* Author: Fabio Mascarenhas
* Version: 1.0
*
* // steffenj: important changes in Lua class:
* - removed all Open*Lib() functions
* - all libs automatically open in the Lua class constructor (just assign nil to unwanted libs)
* */
public class Lua : IDisposable
{
/*readonly */ IntPtr luaState;
ObjectTranslator translator;
LuaFunctionCallback panicCallback;
LuaCSFunction tracebackFunction;
// lockCallback, unlockCallback; used by debug code commented out for now
public Lua()
{
luaState = LuaDLL.luaL_newstate(); // steffenj: Lua 5.1.1 API change (lua_open is gone)
// Load libraries
LuaDLL.luaL_openlibs(luaState);
// Add LuaInterface marker
LuaDLL.lua_pushstring(luaState, "LUAINTERFACE LOADED");
LuaDLL.lua_pushboolean(luaState, true);
LuaDLL.lua_settable(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
translator=new ObjectTranslator(this,luaState);
tracebackFunction = new LuaCSFunction(traceback);
// We need to keep this in a managed reference so the delegate doesn't get garbage collected
panicCallback = new LuaFunctionCallback(PanicCallback);
LuaDLL.lua_atpanic(luaState, panicCallback);
}
private bool _StatePassed;
/*
* CAUTION: LuaInterface.Lua instances can't share the same lua state!
*/
public Lua( Int64 luaState )
{
// Check for existing LuaInterface marker
IntPtr lState = new IntPtr(luaState);
LuaDLL.lua_pushstring(lState, "LUAINTERFACE LOADED");
LuaDLL.lua_gettable(lState, (int)LuaIndexes.LUA_REGISTRYINDEX);
if(LuaDLL.lua_toboolean(lState,-1))
{
LuaDLL.lua_settop(lState,-2);
throw new LuaException("There is already a LuaInterface.Lua instance associated with this Lua state");
}
else
{
// Add LuaInterface marker
LuaDLL.lua_settop(lState,-2);
LuaDLL.lua_pushstring(lState, "LUAINTERFACE LOADED");
LuaDLL.lua_pushboolean(lState, true);
LuaDLL.lua_settable(lState, (int)LuaIndexes.LUA_REGISTRYINDEX);
this.luaState = lState;
LuaDLL.lua_pushvalue(lState, (int)LuaIndexes.LUA_GLOBALSINDEX);
translator = new ObjectTranslator(this, this.luaState);
}
_StatePassed = true;
}
public void Close()
{
if (_StatePassed)
return;
if (luaState != IntPtr.Zero)
LuaDLL.lua_close(luaState);
//luaState = IntPtr.Zero; <- suggested by Christopher Cebulski http://luaforge.net/forum/forum.php?thread_id=44593&forum_id=146
}
static int PanicCallback(IntPtr luaState)
{
// string desc = LuaDLL.lua_tostring(luaState, 1);
string reason = String.Format("unprotected error in call to Lua API ({0})", LuaDLL.lua_tostring(luaState, -1));
// lua_tostring(L, -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>
void ThrowExceptionFromError(int oldTop)
{
object err = translator.getObject(luaState, -1);
LuaDLL.lua_settop(luaState, oldTop);
// A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved)
LuaScriptException 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(), "");
}
/// <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)
{
Exception caughtExcept = e;
if (caughtExcept != null)
{
translator.throwError(luaState, caughtExcept);
LuaDLL.lua_pushnil(luaState);
return 1;
}
else
return 0;
}
private bool executing;
/// <summary>
/// True while a script is being executed
/// </summary>
public bool IsExecuting { get { return executing; } }
/// <summary>
///
/// </summary>
/// <param name="chunk"></param>
/// <param name="name"></param>
/// <returns></returns>
public LuaFunction LoadString(string chunk, string name)
{
int oldTop = LuaDLL.lua_gettop(luaState);
executing = true;
// Somehow, on OS X, we need to use the UTF-8 byte count rather than the string length
// Adapted for OpenRA
var chunkLength = Platform.CurrentPlatform != PlatformType.Windows
? Encoding.UTF8.GetByteCount(chunk)
: chunk.Length;
try
{
if (LuaDLL.luaL_loadbuffer(luaState, chunk, chunkLength, name) != 0)
ThrowExceptionFromError(oldTop);
}
finally { executing = false; }
LuaFunction result = translator.getFunction(luaState, -1);
translator.popValues(luaState, oldTop);
return result;
}
/// <summary>
///
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public LuaFunction LoadFile(string fileName)
{
int oldTop = LuaDLL.lua_gettop(luaState);
if (LuaDLL.luaL_loadfile(luaState, fileName) != 0)
ThrowExceptionFromError(oldTop);
LuaFunction result = translator.getFunction(luaState, -1);
translator.popValues(luaState, oldTop);
return result;
}
/*
* Excutes a Lua chunk and returns all the chunk's return
* values in an array
*/
public object[] DoString(string chunk)
{
return DoString(chunk,"chunk");
}
/// <summary>
/// Executes a Lua chnk 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</param>
/// <returns></returns>
public object[] DoString(string chunk, string chunkName)
{
int oldTop = LuaDLL.lua_gettop(luaState);
executing = true;
// Somehow, on OS X, we need to use the UTF-8 byte count rather than the string length
// Adapted for OpenRA
var chunkLength = Platform.CurrentPlatform != PlatformType.Windows
? Encoding.UTF8.GetByteCount(chunk)
: chunk.Length;
if (LuaDLL.luaL_loadbuffer(luaState, chunk, chunkLength, chunkName) == 0)
{
try
{
if (LuaDLL.lua_pcall(luaState, 0, -1, 0) == 0)
return translator.popValues(luaState, oldTop);
else
ThrowExceptionFromError(oldTop);
}
finally { executing = false; }
}
else
ThrowExceptionFromError(oldTop);
return null; // Never reached - keeps compiler happy
}
private int traceback(IntPtr luaState)
{
LuaDLL.lua_getglobal(luaState,"debug");
LuaDLL.lua_getfield(luaState,-1,"traceback");
LuaDLL.lua_pushvalue(luaState,1);
LuaDLL.lua_pushnumber(luaState,2);
LuaDLL.lua_call (luaState,2,1);
return 1;
}
/*
* Excutes a Lua file and returns all the chunk's return
* values in an array
*/
public object[] DoFile(string fileName)
{
LuaDLL.lua_pushstdcallcfunction(luaState,tracebackFunction);
int oldTop=LuaDLL.lua_gettop(luaState);
if(LuaDLL.luaL_loadfile(luaState,fileName)==0)
{
executing = true;
try
{
if (LuaDLL.lua_pcall(luaState, 0, -1, -2) == 0)
return translator.popValues(luaState, oldTop);
else
ThrowExceptionFromError(oldTop);
}
finally { executing = false; }
}
else
ThrowExceptionFromError(oldTop);
return null; // Never reached - keeps compiler happy
}
public void CollectGarbage()
{
LuaDLL.lua_gc( luaState, LuaGCOptions.LUA_GCCOLLECT, 0 );
}
/*
* 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=LuaDLL.lua_gettop(luaState);
string[] path=fullPath.Split(new char[] { '.' });
LuaDLL.lua_getglobal(luaState,path[0]);
returnValue=translator.getObject(luaState,-1);
if(path.Length>1)
{
string[] remainingPath=new string[path.Length-1];
Array.Copy(path,1,remainingPath,0,path.Length-1);
returnValue=getObject(remainingPath);
}
LuaDLL.lua_settop(luaState,oldTop);
return returnValue;
}
set
{
int oldTop=LuaDLL.lua_gettop(luaState);
string[] path=fullPath.Split(new char[] { '.' });
if(path.Length==1)
{
translator.push(luaState,value);
LuaDLL.lua_setglobal(luaState,fullPath);
}
else
{
LuaDLL.lua_getglobal(luaState,path[0]);
string[] remainingPath=new string[path.Length-1];
Array.Copy(path,1,remainingPath,0,path.Length-1);
setObject(remainingPath,value);
}
LuaDLL.lua_settop(luaState,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
private readonly List<string> globals = new List<string>();
private bool globalsSorted;
/// <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;
}
}
/// <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(LuaCSFunction))
{
// 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 (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))
{
if (
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
(method.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
(method.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0) &&
// Exclude some generic .NET methods that wouldn't be very usefull in Lua
method.Name != "GetType" && method.Name != "GetHashCode" && method.Name != "Equals" &&
method.Name != "ToString" && method.Name != "Clone" && method.Name != "Dispose" &&
method.Name != "GetEnumerator" && method.Name != "CopyTo" &&
!method.Name.StartsWith("get_", StringComparison.Ordinal) &&
!method.Name.StartsWith("set_", StringComparison.Ordinal) &&
!method.Name.StartsWith("add_", StringComparison.Ordinal) &&
!method.Name.StartsWith("remove_", StringComparison.Ordinal))
{
// Format for easy method invocation
string command = path + ":" + method.Name + "(";
if (method.GetParameters().Length == 0) command += ")";
globals.Add(command);
}
}
#endregion
#region Fields
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
{
if (
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
(field.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
(field.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0))
{
// Go into recursion for members
registerGlobal(path + "." + field.Name, field.FieldType, recursionCounter + 1);
}
}
#endregion
#region Properties
foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
(property.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
(property.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0)
// Exclude some generic .NET properties that wouldn't be very usefull in Lua
&& property.Name != "Item")
{
// Go into recursion for members
registerGlobal(path + "." + property.Name, property.PropertyType, recursionCounter + 1);
}
}
#endregion
}
// Otherwise simply add the element to the list
else globals.Add(path);
// 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
*/
internal object getObject(string[] remainingPath)
{
object returnValue=null;
for(int i=0;i<remainingPath.Length;i++)
{
LuaDLL.lua_pushstring(luaState,remainingPath[i]);
LuaDLL.lua_gettable(luaState,-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 (string)this[fullPath];
}
/*
* 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];
return (obj is LuaCSFunction ? new LuaFunction((LuaCSFunction)obj, this) : (LuaFunction)obj);
}
/*
* 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=LuaDLL.lua_gettop(luaState);
if(!LuaDLL.lua_checkstack(luaState,args.Length+6))
throw new LuaException("Lua stack overflow");
translator.push(luaState,function);
if(args!=null)
{
nArgs=args.Length;
for(int i=0;i<args.Length;i++)
{
translator.push(luaState,args[i]);
}
}
executing = true;
try
{
int error = LuaDLL.lua_pcall(luaState, nArgs, -1, 0);
if (error != 0)
ThrowExceptionFromError(oldTop);
}
finally { executing = false; }
if(returnTypes != null)
return translator.popValues(luaState,oldTop,returnTypes);
else
return translator.popValues(luaState, oldTop);
}
/*
* Navigates a table to set the value of one of its fields
*/
internal void setObject(string[] remainingPath, object val)
{
for(int i=0; i<remainingPath.Length-1;i++)
{
LuaDLL.lua_pushstring(luaState,remainingPath[i]);
LuaDLL.lua_gettable(luaState,-2);
}
LuaDLL.lua_pushstring(luaState,remainingPath[remainingPath.Length-1]);
translator.push(luaState,val);
LuaDLL.lua_settable(luaState,-3);
}
/*
* Creates a new table as a global variable or as a field
* inside an existing table
*/
public void NewTable(string fullPath)
{
string[] path=fullPath.Split(new char[] { '.' });
int oldTop=LuaDLL.lua_gettop(luaState);
if(path.Length==1)
{
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_setglobal(luaState,fullPath);
}
else
{
LuaDLL.lua_getglobal(luaState,path[0]);
for(int i=1; i<path.Length-1;i++)
{
LuaDLL.lua_pushstring(luaState,path[i]);
LuaDLL.lua_gettable(luaState,-2);
}
LuaDLL.lua_pushstring(luaState,path[path.Length-1]);
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_settable(luaState,-3);
}
LuaDLL.lua_settop(luaState,oldTop);
}
/*
* Creates a new unnamed table
*/
public LuaTable NewTable()
{
int oldTop = LuaDLL.lua_gettop( luaState );
LuaDLL.lua_newtable( luaState );
LuaTable table = translator.getTable( luaState,-1 );
LuaDLL.lua_settop( luaState, oldTop );
table.IsOrphaned = true;
return table;
}
public ListDictionary GetTableDict(LuaTable table)
{
ListDictionary dict = new ListDictionary();
int oldTop = LuaDLL.lua_gettop(luaState);
translator.push(luaState, table);
LuaDLL.lua_pushnil(luaState);
while (LuaDLL.lua_next(luaState, -2) != 0)
{
dict[translator.getObject(luaState, -2)] = translator.getObject(luaState, -1);
LuaDLL.lua_settop(luaState, -2);
}
LuaDLL.lua_settop(luaState, oldTop);
return dict;
}
/*
* Lets go of a previously allocated reference to a table, function
* or userdata
*/
internal void dispose(int reference)
{
if (luaState != IntPtr.Zero) //Fix submitted by Qingrui Li
LuaDLL.lua_unref(luaState,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=LuaDLL.lua_gettop(luaState);
LuaDLL.lua_getref(luaState,reference);
LuaDLL.lua_pushstring(luaState,field);
LuaDLL.lua_rawget(luaState,-2);
object obj=translator.getObject(luaState,-1);
LuaDLL.lua_settop(luaState,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=LuaDLL.lua_gettop(luaState);
LuaDLL.lua_getref(luaState,reference);
object returnValue=getObject(field.Split(new char[] {'.'}));
LuaDLL.lua_settop(luaState,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=LuaDLL.lua_gettop(luaState);
LuaDLL.lua_getref(luaState,reference);
translator.push(luaState,field);
LuaDLL.lua_gettable(luaState,-2);
object returnValue=translator.getObject(luaState,-1);
LuaDLL.lua_settop(luaState,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=LuaDLL.lua_gettop(luaState);
LuaDLL.lua_getref(luaState,reference);
setObject(field.Split(new char[] {'.'}),val);
LuaDLL.lua_settop(luaState,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=LuaDLL.lua_gettop(luaState);
LuaDLL.lua_getref(luaState,reference);
translator.push(luaState,field);
translator.push(luaState,val);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_settop(luaState,oldTop);
}
/*
* 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 = LuaDLL.lua_gettop(luaState);
LuaMethodWrapper wrapper=new LuaMethodWrapper(translator,target,function.DeclaringType,function);
translator.push(luaState,new LuaCSFunction(wrapper.call));
this[path]=translator.getObject(luaState,-1);
LuaFunction f = GetFunction(path);
LuaDLL.lua_settop(luaState, oldTop);
return f;
}
/*
* Compares the two values referenced by ref1 and ref2 for equality
*/
internal bool compareRef(int ref1, int ref2)
{
int top=LuaDLL.lua_gettop(luaState);
LuaDLL.lua_getref(luaState,ref1);
LuaDLL.lua_getref(luaState,ref2);
int equal=LuaDLL.lua_equal(luaState,-1,-2);
LuaDLL.lua_settop(luaState,top);
return (equal!=0);
}
internal void pushCSFunction(LuaCSFunction function)
{
translator.pushFunction(luaState,function);
}
#region IDisposable Members
public virtual void Dispose()
{
if (translator != null)
{
translator.pendingEvents.Dispose();
translator = null;
}
this.Close();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
}
#endregion
}
}

56
LuaInterface/LuaBase.cs Normal file
View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LuaInterface
{
/// <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.dispose(_Reference);
}
_Interpreter = null;
_Disposed = true;
}
}
public override bool Equals(object o)
{
if (o is LuaBase)
{
LuaBase l = (LuaBase)o;
return _Interpreter.compareRef(l._Reference, _Reference);
}
else return false;
}
public override int GetHashCode()
{
return _Reference;
}
}
}

456
LuaInterface/LuaDLL.cs Normal file
View File

@@ -0,0 +1,456 @@
namespace LuaInterface
{
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Collections;
using System.Text;
using System.Security;
/*
* Lua types for the API, returned by lua_type function
*/
public enum LuaTypes
{
LUA_TNONE=-1,
LUA_TNIL=0,
LUA_TNUMBER=3,
LUA_TSTRING=4,
LUA_TBOOLEAN=1,
LUA_TTABLE=5,
LUA_TFUNCTION=6,
LUA_TUSERDATA=7,
LUA_TLIGHTUSERDATA=2
}
// steffenj: BEGIN lua garbage collector options
/*
* Lua Garbage Collector options (param "what")
*/
public enum LuaGCOptions
{
LUA_GCSTOP = 0,
LUA_GCRESTART = 1,
LUA_GCCOLLECT = 2,
LUA_GCCOUNT = 3,
LUA_GCCOUNTB = 4,
LUA_GCSTEP = 5,
LUA_GCSETPAUSE = 6,
LUA_GCSETSTEPMUL = 7,
}
/*
sealed class LuaGCOptions
{
public static int LUA_GCSTOP = 0;
public static int LUA_GCRESTART = 1;
public static int LUA_GCCOLLECT = 2;
public static int LUA_GCCOUNT = 3;
public static int LUA_GCCOUNTB = 4;
public static int LUA_GCSTEP = 5;
public static int LUA_GCSETPAUSE = 6;
public static int LUA_GCSETSTEPMUL = 7;
};
*/
// steffenj: END lua garbage collector options
/*
* Special stack indexes
*/
sealed class LuaIndexes
{
public static int LUA_REGISTRYINDEX=-10000;
public static int LUA_ENVIRONINDEX=-10001; // steffenj: added environindex
public static int LUA_GLOBALSINDEX=-10002; // steffenj: globalsindex previously was -10001
}
/*
* Structure used by the chunk reader
*/
[ StructLayout( LayoutKind.Sequential )]
public struct ReaderInfo
{
public String chunkData;
public bool finished;
}
/*
* Delegate for functions passed to Lua as function pointers
*/
public delegate int LuaCSFunction(IntPtr luaState);
/*
* Delegate for chunk readers used with lua_load
*/
public delegate string LuaChunkReader(IntPtr luaState,ref ReaderInfo data,ref uint size);
/// <summary>
/// Used to handle Lua panics
/// </summary>
/// <param name="luaState"></param>
/// <returns></returns>
public delegate int LuaFunctionCallback(IntPtr luaState);
/*
* P/Invoke wrapper of the Lua API
*
* Author: Fabio Mascarenhas
* Version: 1.0
*
* // steffenj: noteable changes in the LuaDLL API:
* - luaopen_* functions are gone
* (however Lua class constructor already calls luaL_openlibs now, so just remove these calls)
* - deprecated functions: lua_open, lua_strlen, lua_dostring
* (they still work but may be removed with next Lua version)
*
* list of functions of the Lua 5.1.1 C API that are not in LuaDLL
* i thought this may come in handy for the next Lua version upgrade and for anyone to see
* what the differences are in the APIs (C API vs LuaDLL API)
lua_concat (use System.String concatenation or similar)
lua_cpcall (no point in calling C functions)
lua_dump (would write to unmanaged memory via lua_Writer)
lua_getallocf (no C functions/pointers)
lua_isthread (no threads)
lua_newstate (use luaL_newstate)
lua_newthread (no threads)
lua_pushcclosure (no C functions/pointers)
lua_pushcfunction (no C functions/pointers)
lua_pushfstring (use lua_pushstring)
lua_pushthread (no threads)
lua_pushvfstring (use lua_pushstring)
lua_register (all libs already opened, use require in scripts for external libs)
lua_resume (no threads)
lua_setallocf (no C functions/pointers)
lua_status (no threads)
lua_tointeger (use System.Convert)
lua_tolstring (use lua_tostring)
lua_topointer (no C functions/pointers)
lua_tothread (no threads)
lua_xmove (no threads)
lua_yield (no threads)
luaL_add* (use System.String concatenation or similar)
luaL_argcheck (function argument checking unnecessary)
luaL_argerror (function argument checking unnecessary)
luaL_buffinit (use System.String concatenation or similar)
luaL_checkany (function argument checking unnecessary)
luaL_checkint (function argument checking unnecessary)
luaL_checkinteger (function argument checking unnecessary)
luaL_checklong (function argument checking unnecessary)
luaL_checklstring (function argument checking unnecessary)
luaL_checknumber (function argument checking unnecessary)
luaL_checkoption (function argument checking unnecessary)
luaL_checkstring (function argument checking unnecessary)
luaL_checktype (function argument checking unnecessary)
luaL_prepbuffer (use System.String concatenation or similar)
luaL_pushresult (use System.String concatenation or similar)
luaL_register (all libs already opened, use require in scripts for external libs)
luaL_typerror (function argument checking unnecessary)
(complete lua_Debug interface omitted)
lua_gethook***
lua_getinfo
lua_getlocal
lua_getstack
lua_getupvalue
lua_sethook
lua_setlocal
lua_setupvalue
*/
public class LuaDLL
{
// for debugging
// const string BASEPATH = @"C:\development\software\dotnet\tools\PulseRecognizer\PulseRecognizer\bin\Debug\";
// const string BASEPATH = @"C:\development\software\ThirdParty\lua\Built\";
const string LUADLL = "lua51.dll";
const string LUALIBDLL = LUADLL;
const string STUBDLL = LUADLL;
// steffenj: BEGIN additional Lua API functions new in Lua 5.1
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_gc(IntPtr luaState, LuaGCOptions what, int data);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern string lua_typename(IntPtr luaState, LuaTypes type);
public static string luaL_typename(IntPtr luaState, int stackPos)
{
return LuaDLL.lua_typename(luaState, LuaDLL.lua_type(luaState, stackPos));
}
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void luaL_error(IntPtr luaState, string message);
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern string luaL_gsub(IntPtr luaState, string str, string pattern, string replacement);
// the functions below are still untested
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_getfenv(IntPtr luaState, int stackPos);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_isfunction(IntPtr luaState, int stackPos);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_islightuserdata(IntPtr luaState, int stackPos);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_istable(IntPtr luaState, int stackPos);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_isuserdata(IntPtr luaState, int stackPos);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_lessthan(IntPtr luaState, int stackPos1, int stackPos2);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_rawequal(IntPtr luaState, int stackPos1, int stackPos2);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_setfenv(IntPtr luaState, int stackPos);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_setfield(IntPtr luaState, int stackPos, string name);
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaL_callmeta(IntPtr luaState, int stackPos, string name);
// steffenj: END additional Lua API functions new in Lua 5.1
// steffenj: BEGIN Lua 5.1.1 API change (lua_open replaced by luaL_newstate)
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr luaL_newstate();
/// <summary>DEPRECATED - use luaL_newstate() instead!</summary>
public static IntPtr lua_open()
{
return LuaDLL.luaL_newstate();
}
// steffenj: END Lua 5.1.1 API change (lua_open replaced by luaL_newstate)
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_close(IntPtr luaState);
// steffenj: BEGIN Lua 5.1.1 API change (new function luaL_openlibs)
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void luaL_openlibs(IntPtr luaState);
/*
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void luaopen_base(IntPtr luaState);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaopen_io(IntPtr luaState);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaopen_table(IntPtr luaState);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaopen_string(IntPtr luaState);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaopen_math(IntPtr luaState);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaopen_debug(IntPtr luaState);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaopen_loadlib(IntPtr luaState);
*/
// steffenj: END Lua 5.1.1 API change (new function luaL_openlibs)
// steffenj: BEGIN Lua 5.1.1 API change (lua_strlen is now lua_objlen)
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int lua_objlen(IntPtr luaState, int stackPos);
/// <summary>DEPRECATED - use lua_objlen(IntPtr luaState, int stackPos) instead!</summary>
public static int lua_strlen(IntPtr luaState, int stackPos)
{
return lua_objlen(luaState, stackPos);
}
// steffenj: END Lua 5.1.1 API change (lua_strlen is now lua_objlen)
// steffenj: BEGIN Lua 5.1.1 API change (lua_dostring is now a macro luaL_dostring)
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaL_loadstring(IntPtr luaState, string chunk);
public static int luaL_dostring(IntPtr luaState, string chunk)
{
int result = LuaDLL.luaL_loadstring(luaState, chunk);
if (result != 0)
return result;
return LuaDLL.lua_pcall(luaState, 0, -1, 0);
}
/// <summary>DEPRECATED - use luaL_dostring(IntPtr luaState, string chunk) instead!</summary>
public static int lua_dostring(IntPtr luaState, string chunk)
{
return LuaDLL.luaL_dostring(luaState, chunk);
}
// steffenj: END Lua 5.1.1 API change (lua_dostring is now a macro luaL_dostring)
// steffenj: BEGIN Lua 5.1.1 API change (lua_newtable is gone, lua_createtable is new)
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_createtable(IntPtr luaState, int narr, int nrec);
public static void lua_newtable(IntPtr luaState)
{
LuaDLL.lua_createtable(luaState, 0, 0);
}
// steffenj: END Lua 5.1.1 API change (lua_newtable is gone, lua_createtable is new)
// steffenj: BEGIN Lua 5.1.1 API change (lua_dofile now in LuaLib as luaL_dofile macro)
//[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static int luaL_dofile(IntPtr luaState, string fileName)
{
int result = LuaDLL.luaL_loadfile(luaState, fileName);
if (result != 0)
return result;
return LuaDLL.lua_pcall(luaState, 0, -1, 0);
}
// steffenj: END Lua 5.1.1 API change (lua_dofile now in LuaLib as luaL_dofile)
public static void lua_getglobal(IntPtr luaState, string name)
{
LuaDLL.lua_pushstring(luaState,name);
LuaDLL.lua_gettable(luaState,LuaIndexes.LUA_GLOBALSINDEX);
}
public static void lua_setglobal(IntPtr luaState, string name)
{
LuaDLL.lua_pushstring(luaState,name);
LuaDLL.lua_insert(luaState,-2);
LuaDLL.lua_settable(luaState,LuaIndexes.LUA_GLOBALSINDEX);
}
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_settop(IntPtr luaState, int newTop);
// steffenj: BEGIN added lua_pop "macro"
public static void lua_pop(IntPtr luaState, int amount)
{
LuaDLL.lua_settop(luaState, -(amount) - 1);
}
// steffenj: END added lua_pop "macro"
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_insert(IntPtr luaState, int newTop);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_remove(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_gettable(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_rawget(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_settable(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_rawset(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_setmetatable(IntPtr luaState, int objIndex);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_getmetatable(IntPtr luaState, int objIndex);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_equal(IntPtr luaState, int index1, int index2);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushvalue(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_replace(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_gettop(IntPtr luaState);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern LuaTypes lua_type(IntPtr luaState, int index);
public static bool lua_isnil(IntPtr luaState, int index)
{
return (LuaDLL.lua_type(luaState,index)==LuaTypes.LUA_TNIL);
}
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern bool lua_isnumber(IntPtr luaState, int index);
public static bool lua_isboolean(IntPtr luaState, int index)
{
return LuaDLL.lua_type(luaState,index)==LuaTypes.LUA_TBOOLEAN;
}
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luaL_ref(IntPtr luaState, int registryIndex);
public static int lua_ref(IntPtr luaState, int lockRef)
{
if(lockRef!=0)
{
return LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX);
}
else return 0;
}
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_rawgeti(IntPtr luaState, int tableIndex, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_rawseti(IntPtr luaState, int tableIndex, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr lua_newuserdata(IntPtr luaState, int size);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr lua_touserdata(IntPtr luaState, int index);
public static void lua_getref(IntPtr luaState, int reference)
{
LuaDLL.lua_rawgeti(luaState,LuaIndexes.LUA_REGISTRYINDEX,reference);
}
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaL_unref(IntPtr luaState, int registryIndex, int reference);
public static void lua_unref(IntPtr luaState, int reference)
{
LuaDLL.luaL_unref(luaState,LuaIndexes.LUA_REGISTRYINDEX,reference);
}
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern bool lua_isstring(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern bool lua_iscfunction(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushnil(IntPtr luaState);
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushstdcallcfunction(IntPtr luaState, [MarshalAs(UnmanagedType.FunctionPtr)]LuaCSFunction function);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_call(IntPtr luaState, int nArgs, int nResults);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_pcall(IntPtr luaState, int nArgs, int nResults, int errfunc);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_rawcall(IntPtr luaState, int nArgs, int nResults);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr lua_tocfunction(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern double lua_tonumber(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern bool lua_toboolean(IntPtr luaState, int index);
[DllImport(LUADLL,CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr lua_tolstring(IntPtr luaState, int index, out int strLen);
public static string lua_tostring(IntPtr luaState, int index)
{
int strlen;
IntPtr str = lua_tolstring(luaState, index, out strlen);
if (str != IntPtr.Zero)
return Marshal.PtrToStringAnsi(str, strlen);
else
return null; // treat lua nulls to as C# nulls
}
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_atpanic(IntPtr luaState, LuaFunctionCallback panicf);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushnumber(IntPtr luaState, double number);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushboolean(IntPtr luaState, bool value);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushlstring(IntPtr luaState, string str, int size);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushstring(IntPtr luaState, string str);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luaL_newmetatable(IntPtr luaState, string meta);
// steffenj: BEGIN Lua 5.1.1 API change (luaL_getmetatable is now a macro using lua_getfield)
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_getfield(IntPtr luaState, int stackPos, string meta);
public static void luaL_getmetatable(IntPtr luaState, string meta)
{
LuaDLL.lua_getfield(luaState, LuaIndexes.LUA_REGISTRYINDEX, meta);
}
// steffenj: END Lua 5.1.1 API change (luaL_getmetatable is now a macro using lua_getfield)
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr luaL_checkudata(IntPtr luaState, int stackPos, string meta);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern bool luaL_getmetafield(IntPtr luaState, int stackPos, string field);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_load(IntPtr luaState, LuaChunkReader chunkReader, ref ReaderInfo data, string chunkName);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luaL_loadbuffer(IntPtr luaState, string buff, int size, string name);
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luaL_loadfile(IntPtr luaState, string filename);
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern bool luaL_checkmetatable(IntPtr luaState,int obj);
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luanet_tonetobject(IntPtr luaState,int obj);
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luanet_newudata(IntPtr luaState,int val);
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luanet_rawnetobj(IntPtr luaState,int obj);
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int luanet_checkudata(IntPtr luaState,int obj,string meta);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_error(IntPtr luaState);
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern bool lua_checkstack(IntPtr luaState,int extra);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern int lua_next(IntPtr luaState,int index);
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void lua_pushlightuserdata(IntPtr luaState, IntPtr udata);
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr luanet_gettag();
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
public static extern void luaL_where (IntPtr luaState, int level);
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Runtime.Serialization;
namespace LuaInterface
{
/// <summary>
/// Exceptions thrown by the Lua runtime
/// </summary>
[Serializable]
public class LuaException : Exception
{
public LuaException()
{}
public LuaException(string message) : base(message)
{}
public LuaException(string message, Exception innerException) : base(message, innerException)
{}
protected LuaException(SerializationInfo info, StreamingContext context) : base(info, context)
{}
public override string ToString()
{
return Message;
}
}
}

115
LuaInterface/LuaFunction.cs Normal file
View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LuaInterface
{
public class LuaFunction : LuaBase
{
//private Lua interpreter;
internal LuaCSFunction function;
//internal int reference;
public LuaFunction(int reference, Lua interpreter)
{
_Reference = reference;
this.function = null;
_Interpreter = interpreter;
}
public LuaFunction(LuaCSFunction function, Lua interpreter)
{
_Reference = 0;
this.function = function;
_Interpreter = interpreter;
}
//~LuaFunction()
//{
// if (reference != 0)
// interpreter.dispose(reference);
//}
//bool disposed = false;
//~LuaFunction()
//{
// Dispose(false);
//}
//public void Dispose()
//{
// Dispose(true);
// GC.SuppressFinalize(this);
//}
//public virtual void Dispose(bool disposeManagedResources)
//{
// if (!this.disposed)
// {
// if (disposeManagedResources)
// {
// if (_Reference != 0)
// _Interpreter.dispose(_Reference);
// }
// disposed = true;
// }
//}
/*
* 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(IntPtr luaState)
{
if (_Reference != 0)
LuaDLL.lua_getref(luaState, _Reference);
else
_Interpreter.pushCSFunction(function);
}
public override string ToString()
{
return "function";
}
public override bool Equals(object o)
{
if (o is LuaFunction)
{
LuaFunction 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()
{
if (_Reference != 0)
// elisee: Used to return _Reference
// which doesn't make sense as you can have different refs
// to the same function
return 0;
else
return function.GetHashCode();
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
namespace LuaInterface
{
/// <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; }
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace LuaInterface
{
/// <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
{}
}

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<ProjectGuid>{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}</ProjectGuid>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<OutputPath>..\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="CheckType.cs" />
<Compile Include="GenerateEventAssembly.cs" />
<Compile Include="Lua.cs" />
<Compile Include="LuaBase.cs" />
<Compile Include="LuaDLL.cs" />
<Compile Include="LuaException.cs" />
<Compile Include="LuaFunction.cs" />
<Compile Include="LuaGlobalAttribute.cs" />
<Compile Include="LuaHideAttribute.cs" />
<Compile Include="LuaRegistrationHelper.cs" />
<Compile Include="LuaScriptException.cs" />
<Compile Include="LuaTable.cs" />
<Compile Include="LuaUserData.cs" />
<Compile Include="MethodWrapper.cs" />
<Compile Include="ObjectTranslator.cs" />
<Compile Include="ProxyType.cs" />
<Compile Include="Metatables.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
<Project>{bdaeab25-991e-46a7-af1e-4f0e03358daa}</Project>
<Name>OpenRA.FileFormats</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,90 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace LuaInterface
{
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 (MethodInfo 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 (MethodInfo 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
#region Enumeration
/// <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)
{
#region Sanity checks
if (lua == null) throw new ArgumentNullException("lua");
#endregion
Type type = typeof(T);
if (!type.IsEnum) throw new ArgumentException("The type must be an enumeration!");
string[] names = Enum.GetNames(type);
T[] 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];
}
}
#endregion
}
}

View File

@@ -0,0 +1,50 @@
using System;
namespace LuaInterface
{
/// <summary>
/// Exceptions thrown by the Lua runtime because of errors in the script
/// </summary>
public class LuaScriptException : LuaException
{
/// <summary>
/// Returns true if the exception has occured as the result of a .NET exception in user code
/// </summary>
public bool IsNetException { get; private set; }
private readonly string source;
/// <summary>
/// The position in the script where the exception was triggered.
/// </summary>
public override string Source { get { return source; } }
/// <summary>
/// Creates a new Lua-only exception.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="source">The position in the script where the exception was triggered.</param>
public LuaScriptException(string message, string source) : base(message)
{
this.source = source;
}
/// <summary>
/// Creates a new .NET wrapping exception.
/// </summary>
/// <param name="innerException">The .NET exception triggered by user-code.</param>
/// <param name="source">The position in the script where the exception was triggered.</param>
public LuaScriptException(Exception innerException, string source)
: base(innerException.Message, innerException)
{
this.source = source;
this.IsNetException = true;
}
public override string ToString()
{
// Prepend the error source
return GetType().FullName + ": " + source + Message;
}
}
}

145
LuaInterface/LuaTable.cs Normal file
View File

@@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace LuaInterface
{
/*
* Wrapper class for Lua tables
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
public class LuaTable : LuaBase
{
public bool IsOrphaned;
//internal int _Reference;
//private Lua _Interpreter;
public LuaTable(int reference, Lua interpreter)
{
_Reference = reference;
_Interpreter = interpreter;
}
//bool disposed = false;
//~LuaTable()
//{
// Dispose(false);
//}
//public void Dispose()
//{
// Dispose(true);
// GC.SuppressFinalize(this);
//}
//public virtual void Dispose(bool disposeManagedResources)
//{
// if (!this.disposed)
// {
// if (disposeManagedResources)
// {
// if (_Reference != 0)
// _Interpreter.dispose(_Reference);
// }
// disposed = true;
// }
//}
//~LuaTable()
//{
// _Interpreter.dispose(_Reference);
//}
/*
* 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 System.Collections.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);
}
internal object rawgetFunction(string field)
{
object obj = _Interpreter.rawGetObject(_Reference, field);
if (obj is LuaCSFunction)
return new LuaFunction((LuaCSFunction)obj, _Interpreter);
else
return obj;
}
/*
* Pushes this table into the Lua stack
*/
internal void push(IntPtr luaState)
{
LuaDLL.lua_getref(luaState, _Reference);
}
public override string ToString()
{
return "table";
}
//public override bool Equals(object o)
//{
// if (o is LuaTable)
// {
// LuaTable l = (LuaTable)o;
// return _Interpreter.compareRef(l._Reference, _Reference);
// }
// else return false;
//}
//public override int GetHashCode()
//{
// return _Reference;
//}
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LuaInterface
{
public class LuaUserData : LuaBase
{
//internal int _Reference;
//private Lua _Interpreter;
public LuaUserData(int reference, Lua interpreter)
{
_Reference = reference;
_Interpreter = interpreter;
}
//~LuaUserData()
//{
// if (_Reference != 0)
// _Interpreter.dispose(_Reference);
//}
/*
* 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);
}
/*
* Pushes the userdata into the Lua stack
*/
internal void push(IntPtr luaState)
{
LuaDLL.lua_getref(luaState, _Reference);
}
public override string ToString()
{
return "userdata";
}
//public override bool Equals(object o)
//{
// if (o is LuaUserData)
// {
// LuaUserData l = (LuaUserData)o;
// return _Interpreter.compareRef(l._Reference, _Reference);
// }
// else return false;
//}
//public override int GetHashCode()
//{
// return _Reference;
//}
}
}

935
LuaInterface/Metatables.cs Normal file
View File

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

View File

@@ -0,0 +1,595 @@
namespace LuaInterface
{
using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Collections.Generic;
using System.Diagnostics;
/*
* Cached method
*/
struct MethodCache
{
private MethodBase _cachedMethod;
public MethodBase cachedMethod
{
get
{
return _cachedMethod;
}
set
{
_cachedMethod = value;
MethodInfo mi = value as MethodInfo;
if (mi != null)
{
//SJD this is guaranteed to be correct irrespective of actual name used for type..
IsReturnVoid = mi.ReturnType == typeof(void);
}
}
}
public bool IsReturnVoid;
// List or arguments
public object[] args;
// Positions of out parameters
public int[] outList;
// Types of parameters
public MethodArgs[] argTypes;
}
/*
* Parameter information
*/
struct MethodArgs
{
// Position of parameter
public int index;
// Type-conversion function
public ExtractValue extractValue;
public bool isParamsArray;
public Type paramsArrayType;
}
/*
* Argument extraction with type-conversion function
*/
delegate object ExtractValue(IntPtr luaState, int stackPos);
/*
* Wrapper class for methods/constructors accessed from Lua.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class LuaMethodWrapper
{
private ObjectTranslator _Translator;
private MethodBase _Method;
private MethodCache _LastCalledMethod = new MethodCache();
private string _MethodName;
private MemberInfo[] _Members;
private ExtractValue _ExtractTarget;
private object _Target;
private BindingFlags _BindingType;
/*
* Constructs the wrapper for a known MethodBase instance
*/
public LuaMethodWrapper(ObjectTranslator translator, object target, IReflect targetType, MethodBase method)
{
_Translator = translator;
_Target = target;
if (targetType != null)
_ExtractTarget = translator.typeChecker.getExtractor(targetType);
_Method = method;
_MethodName = method.Name;
if (method.IsStatic)
{ _BindingType = BindingFlags.Static; }
else
{ _BindingType = BindingFlags.Instance; }
}
/*
* Constructs the wrapper for a known method name
*/
public LuaMethodWrapper(ObjectTranslator translator, IReflect targetType, string methodName, BindingFlags bindingType)
{
_Translator = translator;
_MethodName = methodName;
if (targetType != null)
_ExtractTarget = translator.typeChecker.getExtractor(targetType);
_BindingType = bindingType;
//CP: Removed NonPublic binding search and added IgnoreCase
_Members = targetType.UnderlyingSystemType.GetMember(methodName, MemberTypes.Method, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*|BindingFlags.NonPublic*/);
}
/// <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);
}
private static bool IsInteger(double x) {
return Math.Ceiling(x) == x;
}
/*
* Calls the method. Receives the arguments from the Lua stack
* and returns values in it.
*/
public int call(IntPtr luaState)
{
MethodBase methodToCall = _Method;
object targetObject = _Target;
bool failedCall = true;
int nReturnValues = 0;
if (!LuaDLL.lua_checkstack(luaState, 5))
throw new LuaException("Lua stack overflow");
bool isStatic = (_BindingType & BindingFlags.Static) == BindingFlags.Static;
SetPendingException(null);
if (methodToCall == null) // Method from name
{
if (isStatic)
targetObject = null;
else
targetObject = _ExtractTarget(luaState, 1);
//LuaDLL.lua_remove(luaState,1); // Pops the receiver
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 = LuaDLL.lua_gettop(luaState) - numStackToSkip;
MethodBase method = _LastCalledMethod.cachedMethod;
if (numArgsPassed == _LastCalledMethod.argTypes.Length) // No. of args match?
{
if (!LuaDLL.lua_checkstack(luaState, _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];
object luaParamValue = type.extractValue(luaState, i + 1 + numStackToSkip);
if (_LastCalledMethod.argTypes[i].isParamsArray)
{
args[type.index] = _Translator.tableToArray(luaParamValue,type.paramsArrayType);
}
else
{
args[type.index] = luaParamValue;
}
if (args[type.index] == null &&
!LuaDLL.lua_isnil(luaState, i + 1 + numStackToSkip))
{
throw new LuaException("argument number " + (i + 1) + " is invalid");
}
}
if ((_BindingType & BindingFlags.Static) == BindingFlags.Static)
{
_Translator.push(luaState, method.Invoke(null, args));
}
else
{
if (_LastCalledMethod.cachedMethod.IsConstructor)
_Translator.push(luaState, ((ConstructorInfo)method).Invoke(args));
else
_Translator.push(luaState, method.Invoke(targetObject,args));
}
failedCall = false;
}
catch (TargetInvocationException e)
{
// Failure of method invocation
return SetPendingException(e.GetBaseException());
}
catch (Exception e)
{
if (_Members.Length == 1) // Is the method overloaded?
// No, throw error
return SetPendingException(e);
}
}
}
// Cache miss
if (failedCall)
{
// System.Diagnostics.Debug.WriteLine("cache miss on " + methodName);
// If we are running an instance variable, we can now pop the targetObject from the stack
if (!isStatic)
{
if (targetObject == null)
{
_Translator.throwError(luaState, String.Format("instance method '{0}' requires a non null target object", _MethodName));
LuaDLL.lua_pushnil(luaState);
return 1;
}
LuaDLL.lua_remove(luaState, 1); // Pops the receiver
}
bool hasMatch = false;
string candidateName = null;
foreach (MemberInfo member in _Members)
{
candidateName = member.ReflectedType.Name + "." + member.Name;
MethodBase 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: " + _MethodName
: ("invalid arguments to method: " + candidateName);
_Translator.throwError(luaState, msg);
LuaDLL.lua_pushnil(luaState);
return 1;
}
}
}
else // Method from MethodBase instance
{
if (methodToCall.ContainsGenericParameters)
{
// bool isMethod = //* not used
_Translator.matchParameters(luaState, methodToCall, ref _LastCalledMethod);
if (methodToCall.IsGenericMethodDefinition)
{
//need to make a concrete type of the generic method definition
List<Type> typeArgs = new List<Type>();
foreach (object arg in _LastCalledMethod.args)
typeArgs.Add(arg.GetType());
MethodInfo concreteMethod = (methodToCall as MethodInfo).MakeGenericMethod(typeArgs.ToArray());
_Translator.push(luaState, concreteMethod.Invoke(targetObject, _LastCalledMethod.args));
failedCall = false;
}
else if (methodToCall.ContainsGenericParameters)
{
_Translator.throwError(luaState, "unable to invoke method on generic class as the current method is an open generic method");
LuaDLL.lua_pushnil(luaState);
return 1;
}
}
else
{
if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null)
{
targetObject = _ExtractTarget(luaState, 1);
LuaDLL.lua_remove(luaState, 1); // Pops the receiver
}
if (!_Translator.matchParameters(luaState, methodToCall, ref _LastCalledMethod))
{
_Translator.throwError(luaState, string.Format("invalid arguments to method call {0} of type {1}", methodToCall, methodToCall.ReflectedType));
LuaDLL.lua_pushnil(luaState);
return 1;
}
}
}
if (failedCall)
{
if (!LuaDLL.lua_checkstack(luaState, _LastCalledMethod.outList.Length + 6))
throw new LuaException("Lua stack overflow");
try
{
if (isStatic)
{
_Translator.push(luaState, _LastCalledMethod.cachedMethod.Invoke(null, _LastCalledMethod.args));
}
else
{
if (_LastCalledMethod.cachedMethod.IsConstructor)
_Translator.push(luaState, ((ConstructorInfo)_LastCalledMethod.cachedMethod).Invoke(_LastCalledMethod.args));
else
{
object returnValue = _LastCalledMethod.cachedMethod.Invoke( targetObject, _LastCalledMethod.args );
_Translator.push(luaState, returnValue );
LuaTable returnValueLuaBase = returnValue as LuaTable;
if( returnValueLuaBase != null && returnValueLuaBase.IsOrphaned )
{
returnValueLuaBase.Dispose();
}
}
}
}
catch (TargetInvocationException e)
{
return SetPendingException(e.GetBaseException());
}
catch (Exception e)
{
return SetPendingException(e);
}
}
// Pushes out and ref return values
for (int index = 0; index < _LastCalledMethod.outList.Length; index++)
{
nReturnValues++;
object outArg = _LastCalledMethod.args[_LastCalledMethod.outList[index]];
_Translator.push(luaState, outArg );
LuaTable outArgLuaBase = outArg as LuaTable;
if( outArgLuaBase != null && outArgLuaBase.IsOrphaned )
{
outArgLuaBase.Dispose();
}
}
//by isSingle 2010-09-10 11:26:31
//Desc:
// 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;
}
}
/// <summary>
/// We keep track of what delegates we have auto attached to an event - to allow us to cleanly exit a LuaInterface session
/// </summary>
class EventHandlerContainer : IDisposable
{
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();
}
}
/*
* Wrapper class for events that does registration/deregistration
* of event handlers.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class RegisterEventHandler
{
object target;
EventInfo eventInfo;
EventHandlerContainer pendingEvents;
public RegisterEventHandler(EventHandlerContainer pendingEvents, object target, EventInfo eventInfo)
{
this.target = target;
this.eventInfo = eventInfo;
this.pendingEvents = pendingEvents;
}
/*
* Adds a new event handler
*/
public Delegate Add(LuaFunction function)
{
//CP: Fix by Ben Bryant for event handling with one parameter
//link: http://luaforge.net/forum/message.php?msg_id=9266
Delegate handlerDelegate = CodeGeneration.Instance.GetDelegate(eventInfo.EventHandlerType, function);
eventInfo.AddEventHandler(target, handlerDelegate);
pendingEvents.Add(handlerDelegate, this);
return handlerDelegate;
//MethodInfo mi = eventInfo.EventHandlerType.GetMethod("Invoke");
//ParameterInfo[] pi = mi.GetParameters();
//LuaEventHandler handler=CodeGeneration.Instance.GetEvent(pi[1].ParameterType,function);
//Delegate handlerDelegate=Delegate.CreateDelegate(eventInfo.EventHandlerType,handler,"HandleEvent");
//eventInfo.AddEventHandler(target,handlerDelegate);
//pendingEvents.Add(handlerDelegate, this);
//return handlerDelegate;
}
/*
* Removes an existing event handler
*/
public void Remove(Delegate handlerDelegate)
{
RemovePending(handlerDelegate);
pendingEvents.Remove(handlerDelegate);
}
/*
* Removes an existing event handler (without updating the pending handlers list)
*/
internal void RemovePending(Delegate handlerDelegate)
{
eventInfo.RemoveEventHandler(target, handlerDelegate);
}
}
/*
* Base wrapper class for Lua function event handlers.
* Subclasses that do actual event handling are created
* at runtime.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
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);
}
//public void handleEvent(object sender,object data)
//{
// handler.call(new object[] { sender,data },new Type[0]);
//}
}
/*
* Wrapper class for Lua functions as delegates
* Subclasses with correct signatures are created
* at runtime.
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
public class LuaDelegate
{
public Type[] returnTypes;
public LuaFunction function;
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;
}
}
/*
* 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)
{
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;
}
}
}

View File

@@ -0,0 +1,870 @@
namespace LuaInterface
{
using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Collections.Generic;
using System.Diagnostics;
/*
* Passes objects from the CLR to Lua and vice-versa
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
public class ObjectTranslator
{
internal CheckType typeChecker;
// object # to object (FIXME - it should be possible to get object address as an object #)
public readonly Dictionary<int, object> objects = new Dictionary<int, object>();
// object to object #
public readonly Dictionary<object, int> objectsBackMap = new Dictionary<object, int>();
internal Lua interpreter;
private MetaFunctions metaFunctions;
private List<Assembly> assemblies;
private LuaCSFunction getMethodSigFunction, getConstructorSigFunction, ctypeFunction, enumFromIntFunction;
internal EventHandlerContainer pendingEvents = new EventHandlerContainer();
public ObjectTranslator(Lua interpreter,IntPtr luaState)
{
this.interpreter = interpreter;
typeChecker = new CheckType(this);
metaFunctions = new MetaFunctions(this);
assemblies = new List<Assembly>();
getMethodSigFunction= getMethodSignature;
getConstructorSigFunction= getConstructorSignature;
ctypeFunction = ctype;
enumFromIntFunction = enumFromInt;
createLuaObjectList(luaState);
createIndexingMetaFunction(luaState);
createBaseClassMetatable(luaState);
createClassMetatable(luaState);
createFunctionMetatable(luaState);
setGlobalFunctions(luaState);
}
/*
* Sets up the list of objects in the Lua side
*/
private void createLuaObjectList(IntPtr luaState)
{
LuaDLL.lua_pushstring(luaState,"luaNet_objects");
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_pushstring(luaState,"__mode");
LuaDLL.lua_pushstring(luaState,"v");
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_setmetatable(luaState,-2);
LuaDLL.lua_settable(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
}
/*
* Registers the indexing function of CLR objects
* passed to Lua
*/
private void createIndexingMetaFunction(IntPtr luaState)
{
LuaDLL.lua_pushstring(luaState,"luaNet_indexfunction");
LuaDLL.luaL_dostring(luaState,MetaFunctions.luaIndexFunction); // steffenj: lua_dostring renamed to luaL_dostring
//LuaDLL.lua_pushstdcallcfunction(luaState,indexFunction);
LuaDLL.lua_rawset(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
}
/*
* Creates the metatable for superclasses (the base
* field of registered tables)
*/
private void createBaseClassMetatable(IntPtr luaState)
{
LuaDLL.luaL_newmetatable(luaState,"luaNet_searchbase");
LuaDLL.lua_pushstring(luaState,"__gc");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__tostring");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__index");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.baseIndexFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__newindex");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.newindexFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_settop(luaState,-2);
}
/*
* Creates the metatable for type references
*/
private void createClassMetatable(IntPtr luaState)
{
LuaDLL.luaL_newmetatable(luaState,"luaNet_class");
LuaDLL.lua_pushstring(luaState,"__gc");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__tostring");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__index");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classIndexFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__newindex");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classNewindexFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__call");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.callConstructorFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_settop(luaState,-2);
}
/*
* Registers the global functions used by LuaInterface
*/
private void setGlobalFunctions(IntPtr luaState)
{
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.indexFunction);
LuaDLL.lua_setglobal(luaState,"get_object_member");
/*LuaDLL.lua_pushstdcallcfunction(luaState,importTypeFunction);
LuaDLL.lua_setglobal(luaState,"import_type");
LuaDLL.lua_pushstdcallcfunction(luaState,loadAssemblyFunction);
LuaDLL.lua_setglobal(luaState,"load_assembly");
LuaDLL.lua_pushstdcallcfunction(luaState,registerTableFunction);
LuaDLL.lua_setglobal(luaState,"make_object");
LuaDLL.lua_pushstdcallcfunction(luaState,unregisterTableFunction);
LuaDLL.lua_setglobal(luaState,"free_object");*/
LuaDLL.lua_pushstdcallcfunction(luaState,getMethodSigFunction);
LuaDLL.lua_setglobal(luaState,"get_method_bysig");
LuaDLL.lua_pushstdcallcfunction(luaState,getConstructorSigFunction);
LuaDLL.lua_setglobal(luaState,"get_constructor_bysig");
LuaDLL.lua_pushstdcallcfunction(luaState,ctypeFunction);
LuaDLL.lua_setglobal(luaState,"ctype");
LuaDLL.lua_pushstdcallcfunction(luaState,enumFromIntFunction);
LuaDLL.lua_setglobal(luaState,"enum");
}
/*
* Creates the metatable for delegates
*/
private void createFunctionMetatable(IntPtr luaState)
{
LuaDLL.luaL_newmetatable(luaState,"luaNet_function");
LuaDLL.lua_pushstring(luaState,"__gc");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__call");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.execDelegateFunction);
LuaDLL.lua_settable(luaState,-3);
LuaDLL.lua_settop(luaState,-2);
}
/*
* Passes errors (argument e) to the Lua interpreter
*/
internal void throwError(IntPtr luaState, object e)
{
// We use this to remove anything pushed by luaL_where
int oldTop = LuaDLL.lua_gettop(luaState);
// Stack frame #1 is our C# wrapper, so not very interesting to the user
// Stack frame #2 must be the lua code that called us, so that's what we want to use
LuaDLL.luaL_where(luaState, 1);
object[] curlev = popValues(luaState, oldTop);
// Determine the position in the script where the exception was triggered
string errLocation = "";
if (curlev.Length > 0)
errLocation = curlev[0].ToString();
string message = e as string;
if (message != null)
{
// Wrap Lua error (just a string) and store the error location
e = new LuaScriptException(message, errLocation);
}
else
{
Exception ex = e as Exception;
if (ex != null)
{
// Wrap generic .NET exception as an InnerException and store the error location
e = new LuaScriptException(ex, errLocation);
}
}
push(luaState, e);
LuaDLL.lua_error(luaState);
}
/*
* Implementation of load_assembly. Throws an error
* if the assembly is not found.
*/
private int loadAssembly(IntPtr luaState)
{
try
{
string assemblyName = LuaDLL.lua_tostring(luaState,1);
Assembly assembly = Assembly.Load(AssemblyName.GetAssemblyName(assemblyName));
if (assembly != null && !assemblies.Contains(assembly))
{
assemblies.Add(assembly);
}
}
catch(Exception e)
{
throwError(luaState,e);
}
return 0;
}
internal Type FindType(string className)
{
foreach(Assembly assembly in assemblies)
{
Type klass=assembly.GetType(className);
if(klass!=null)
{
return klass;
}
}
return null;
}
/*
* Implementation of import_type. Returns nil if the
* type is not found.
*/
private int importType(IntPtr luaState)
{
string className=LuaDLL.lua_tostring(luaState,1);
Type klass=FindType(className);
if(klass!=null)
pushType(luaState,klass);
else
LuaDLL.lua_pushnil(luaState);
return 1;
}
/*
* Implementation of make_object. Registers a table (first
* argument in the stack) as an object subclassing the
* type passed as second argument in the stack.
*/
private int registerTable(IntPtr luaState)
{
if(LuaDLL.lua_type(luaState,1)==LuaTypes.LUA_TTABLE)
{
LuaTable luaTable=getTable(luaState,1);
string superclassName = LuaDLL.lua_tostring(luaState, 2);
if (superclassName != null)
{
Type klass = FindType(superclassName);
if (klass != null)
{
// Creates and pushes the object in the stack, setting
// it as the metatable of the first argument
object obj = CodeGeneration.Instance.GetClassInstance(klass, luaTable);
pushObject(luaState, obj, "luaNet_metatable");
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_pushstring(luaState, "__index");
LuaDLL.lua_pushvalue(luaState, -3);
LuaDLL.lua_settable(luaState, -3);
LuaDLL.lua_pushstring(luaState, "__newindex");
LuaDLL.lua_pushvalue(luaState, -3);
LuaDLL.lua_settable(luaState, -3);
LuaDLL.lua_setmetatable(luaState, 1);
// Pushes the object again, this time as the base field
// of the table and with the luaNet_searchbase metatable
LuaDLL.lua_pushstring(luaState, "base");
int index = addObject(obj);
pushNewObject(luaState, obj, index, "luaNet_searchbase");
LuaDLL.lua_rawset(luaState, 1);
}
else
throwError(luaState, "register_table: can not find superclass '" + superclassName + "'");
}
else
throwError(luaState, "register_table: superclass name can not be null");
}
else throwError(luaState,"register_table: first arg is not a table");
return 0;
}
/*
* Implementation of free_object. Clears the metatable and the
* base field, freeing the created object for garbage-collection
*/
private int unregisterTable(IntPtr luaState)
{
try
{
if(LuaDLL.lua_getmetatable(luaState,1)!=0)
{
LuaDLL.lua_pushstring(luaState,"__index");
LuaDLL.lua_gettable(luaState,-2);
object obj=getRawNetObject(luaState,-1);
if(obj==null) throwError(luaState,"unregister_table: arg is not valid table");
FieldInfo luaTableField=obj.GetType().GetField("__luaInterface_luaTable");
if(luaTableField==null) throwError(luaState,"unregister_table: arg is not valid table");
luaTableField.SetValue(obj,null);
LuaDLL.lua_pushnil(luaState);
LuaDLL.lua_setmetatable(luaState,1);
LuaDLL.lua_pushstring(luaState,"base");
LuaDLL.lua_pushnil(luaState);
LuaDLL.lua_settable(luaState,1);
}
else throwError(luaState,"unregister_table: arg is not valid table");
}
catch(Exception e)
{
throwError(luaState,e.Message);
}
return 0;
}
/*
* Implementation of get_method_bysig. Returns nil
* if no matching method is not found.
*/
private int getMethodSignature(IntPtr luaState)
{
IReflect klass; object target;
int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
if(udata!=-1)
{
klass=(IReflect)objects[udata];
target=null;
}
else
{
target=getRawNetObject(luaState,1);
if(target==null)
{
throwError(luaState,"get_method_bysig: first arg is not type or object reference");
LuaDLL.lua_pushnil(luaState);
return 1;
}
klass=target.GetType();
}
string methodName=LuaDLL.lua_tostring(luaState,2);
Type[] signature=new Type[LuaDLL.lua_gettop(luaState)-2];
for(int i=0;i<signature.Length;i++)
signature[i]=FindType(LuaDLL.lua_tostring(luaState,i+3));
try
{
//CP: Added ignore case
MethodInfo method=klass.GetMethod(methodName,BindingFlags.Public | BindingFlags.Static |
BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase, null, signature, null);
pushFunction(luaState,new LuaCSFunction((new LuaMethodWrapper(this,target,klass,method)).call));
}
catch(Exception e)
{
throwError(luaState,e);
LuaDLL.lua_pushnil(luaState);
}
return 1;
}
/*
* Implementation of get_constructor_bysig. Returns nil
* if no matching constructor is found.
*/
private int getConstructorSignature(IntPtr luaState)
{
IReflect klass=null;
int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
if(udata!=-1)
{
klass=(IReflect)objects[udata];
}
if(klass==null)
{
throwError(luaState,"get_constructor_bysig: first arg is invalid type reference");
}
Type[] signature=new Type[LuaDLL.lua_gettop(luaState)-1];
for(int i=0;i<signature.Length;i++)
signature[i]=FindType(LuaDLL.lua_tostring(luaState,i+2));
try
{
ConstructorInfo constructor=klass.UnderlyingSystemType.GetConstructor(signature);
pushFunction(luaState,new LuaCSFunction((new LuaMethodWrapper(this,null,klass,constructor)).call));
}
catch(Exception e)
{
throwError(luaState,e);
LuaDLL.lua_pushnil(luaState);
}
return 1;
}
private Type typeOf(IntPtr luaState, int idx)
{
int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
if (udata == -1) {
return null;
} else {
ProxyType pt = (ProxyType)objects[udata];
return pt.UnderlyingSystemType;
}
}
public int pushError(IntPtr luaState, string msg)
{
LuaDLL.lua_pushnil(luaState);
LuaDLL.lua_pushstring(luaState,msg);
return 2;
}
private int ctype(IntPtr luaState)
{
Type t = typeOf(luaState,1);
if (t == null) {
return pushError(luaState,"not a CLR class");
}
pushObject(luaState,t,"luaNet_metatable");
return 1;
}
private int enumFromInt(IntPtr luaState)
{
Type t = typeOf(luaState,1);
if (t == null || ! t.IsEnum) {
return pushError(luaState,"not an enum");
}
object res = null;
LuaTypes lt = LuaDLL.lua_type(luaState,2);
if (lt == LuaTypes.LUA_TNUMBER) {
int ival = (int)LuaDLL.lua_tonumber(luaState,2);
res = Enum.ToObject(t,ival);
} else
if (lt == LuaTypes.LUA_TSTRING) {
string sflags = LuaDLL.lua_tostring(luaState,2);
string err = null;
try {
res = Enum.Parse(t,sflags);
} catch (ArgumentException e) {
err = e.Message;
}
if (err != null) {
return pushError(luaState,err);
}
} else {
return pushError(luaState,"second argument must be a integer or a string");
}
pushObject(luaState,res,"luaNet_metatable");
return 1;
}
/*
* Pushes a type reference into the stack
*/
internal void pushType(IntPtr luaState, Type t)
{
pushObject(luaState,new ProxyType(t),"luaNet_class");
}
/*
* Pushes a delegate into the stack
*/
internal void pushFunction(IntPtr luaState, LuaCSFunction func)
{
pushObject(luaState,func,"luaNet_function");
}
/*
* Pushes a CLR object into the Lua stack as an userdata
* with the provided metatable
*/
internal void pushObject(IntPtr luaState, object o, string metatable)
{
int index = -1;
// Pushes nil
if(o==null)
{
LuaDLL.lua_pushnil(luaState);
return;
}
// Object already in the list of Lua objects? Push the stored reference.
bool found = objectsBackMap.TryGetValue(o, out index);
if(found)
{
LuaDLL.luaL_getmetatable(luaState,"luaNet_objects");
LuaDLL.lua_rawgeti(luaState,-1,index);
// Note: starting with lua5.1 the garbage collector may remove weak reference items (such as our luaNet_objects values) when the initial GC sweep
// occurs, but the actual call of the __gc finalizer for that object may not happen until a little while later. During that window we might call
// this routine and find the element missing from luaNet_objects, but collectObject() has not yet been called. In that case, we go ahead and call collect
// object here
// did we find a non nil object in our table? if not, we need to call collect object
LuaTypes type = LuaDLL.lua_type(luaState, -1);
if (type != LuaTypes.LUA_TNIL)
{
LuaDLL.lua_remove(luaState, -2); // drop the metatable - we're going to leave our object on the stack
return;
}
// MetaFunctions.dumpStack(this, luaState);
LuaDLL.lua_remove(luaState, -1); // remove the nil object value
LuaDLL.lua_remove(luaState, -1); // remove the metatable
collectObject(o, index); // Remove from both our tables and fall out to get a new ID
}
index = addObject(o);
pushNewObject(luaState,o,index,metatable);
}
/*
* Pushes a new object into the Lua stack with the provided
* metatable
*/
private void pushNewObject(IntPtr luaState,object o,int index,string metatable)
{
if(metatable=="luaNet_metatable")
{
// Gets or creates the metatable for the object's type
LuaDLL.luaL_getmetatable(luaState,o.GetType().AssemblyQualifiedName);
if(LuaDLL.lua_isnil(luaState,-1))
{
LuaDLL.lua_settop(luaState,-2);
LuaDLL.luaL_newmetatable(luaState,o.GetType().AssemblyQualifiedName);
LuaDLL.lua_pushstring(luaState,"cache");
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_rawset(luaState,-3);
LuaDLL.lua_pushlightuserdata(luaState,LuaDLL.luanet_gettag());
LuaDLL.lua_pushnumber(luaState,1);
LuaDLL.lua_rawset(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__index");
LuaDLL.lua_pushstring(luaState,"luaNet_indexfunction");
LuaDLL.lua_rawget(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
LuaDLL.lua_rawset(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__gc");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
LuaDLL.lua_rawset(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__tostring");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
LuaDLL.lua_rawset(luaState,-3);
LuaDLL.lua_pushstring(luaState,"__newindex");
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.newindexFunction);
LuaDLL.lua_rawset(luaState,-3);
}
}
else
{
LuaDLL.luaL_getmetatable(luaState,metatable);
}
// Stores the object index in the Lua list and pushes the
// index into the Lua stack
LuaDLL.luaL_getmetatable(luaState,"luaNet_objects");
LuaDLL.luanet_newudata(luaState,index);
LuaDLL.lua_pushvalue(luaState,-3);
LuaDLL.lua_remove(luaState,-4);
LuaDLL.lua_setmetatable(luaState,-2);
LuaDLL.lua_pushvalue(luaState,-1);
LuaDLL.lua_rawseti(luaState,-3,index);
LuaDLL.lua_remove(luaState,-2);
}
/*
* Gets an object from the Lua stack with the desired type, if it matches, otherwise
* returns null.
*/
internal object getAsType(IntPtr luaState,int stackPos,Type paramType)
{
ExtractValue extractor=typeChecker.checkType(luaState,stackPos,paramType);
if(extractor!=null) return extractor(luaState,stackPos);
return null;
}
/// <summary>
/// Given the Lua int ID for an object remove it from our maps
/// </summary>
/// <param name="udata"></param>
internal void collectObject(int udata)
{
object o;
bool found = objects.TryGetValue(udata, out o);
// The other variant of collectObject might have gotten here first, in that case we will silently ignore the missing entry
if (found)
{
// Debug.WriteLine("Removing " + o.ToString() + " @ " + udata);
objects.Remove(udata);
objectsBackMap.Remove(o);
}
}
/// <summary>
/// Given an object reference, remove it from our maps
/// </summary>
/// <param name="udata"></param>
void collectObject(object o, int udata)
{
// Debug.WriteLine("Removing " + o.ToString() + " @ " + udata);
objects.Remove(udata);
objectsBackMap.Remove(o);
}
/// <summary>
/// We want to ensure that objects always have a unique ID
/// </summary>
int nextObj = 0;
int addObject(object obj)
{
// New object: inserts it in the list
int index = nextObj++;
// Debug.WriteLine("Adding " + obj.ToString() + " @ " + index);
objects[index] = obj;
objectsBackMap[obj] = index;
return index;
}
/*
* Gets an object from the Lua stack according to its Lua type.
*/
internal object getObject(IntPtr luaState,int index)
{
LuaTypes type=LuaDLL.lua_type(luaState,index);
switch(type)
{
case LuaTypes.LUA_TNUMBER:
{
return LuaDLL.lua_tonumber(luaState,index);
}
case LuaTypes.LUA_TSTRING:
{
return LuaDLL.lua_tostring(luaState,index);
}
case LuaTypes.LUA_TBOOLEAN:
{
return LuaDLL.lua_toboolean(luaState,index);
}
case LuaTypes.LUA_TTABLE:
{
return getTable(luaState,index);
}
case LuaTypes.LUA_TFUNCTION:
{
return getFunction(luaState,index);
}
case LuaTypes.LUA_TUSERDATA:
{
int udata=LuaDLL.luanet_tonetobject(luaState,index);
if(udata!=-1)
return objects[udata];
else
return getUserData(luaState,index);
}
default:
return null;
}
}
/*
* Gets the table in the index positon of the Lua stack.
*/
internal LuaTable getTable(IntPtr luaState,int index)
{
LuaDLL.lua_pushvalue(luaState,index);
return new LuaTable(LuaDLL.lua_ref(luaState,1),interpreter);
}
/*
* Gets the userdata in the index positon of the Lua stack.
*/
internal LuaUserData getUserData(IntPtr luaState,int index)
{
LuaDLL.lua_pushvalue(luaState,index);
return new LuaUserData(LuaDLL.lua_ref(luaState,1),interpreter);
}
/*
* Gets the function in the index positon of the Lua stack.
*/
internal LuaFunction getFunction(IntPtr luaState,int index)
{
LuaDLL.lua_pushvalue(luaState,index);
return new LuaFunction(LuaDLL.lua_ref(luaState,1),interpreter);
}
/*
* Gets the CLR object in the index positon of the Lua stack. Returns
* delegates as Lua functions.
*/
internal object getNetObject(IntPtr luaState,int index)
{
int idx=LuaDLL.luanet_tonetobject(luaState,index);
if(idx!=-1)
return objects[idx];
else
return null;
}
/*
* Gets the CLR object in the index positon of the Lua stack. Returns
* delegates as is.
*/
internal object getRawNetObject(IntPtr luaState,int index)
{
int udata=LuaDLL.luanet_rawnetobj(luaState,index);
if(udata!=-1)
{
return objects[udata];
}
return null;
}
/*
* Pushes the entire array into the Lua stack and returns the number
* of elements pushed.
*/
internal int returnValues(IntPtr luaState, object[] returnValues)
{
if(LuaDLL.lua_checkstack(luaState,returnValues.Length+5))
{
for(int i=0;i<returnValues.Length;i++)
{
push(luaState,returnValues[i]);
}
return returnValues.Length;
} else
return 0;
}
/*
* Gets the values from the provided index to
* the top of the stack and returns them in an array.
*/
internal object[] popValues(IntPtr luaState,int oldTop)
{
int newTop=LuaDLL.lua_gettop(luaState);
if(oldTop==newTop)
{
return null;
}
else
{
ArrayList returnValues=new ArrayList();
for(int i=oldTop+1;i<=newTop;i++)
{
returnValues.Add(getObject(luaState,i));
}
LuaDLL.lua_settop(luaState,oldTop);
return returnValues.ToArray();
}
}
/*
* Gets the values from the provided index to
* the top of the stack and returns them in an array, casting
* them to the provided types.
*/
internal object[] popValues(IntPtr luaState,int oldTop,Type[] popTypes)
{
int newTop=LuaDLL.lua_gettop(luaState);
if(oldTop==newTop)
{
return null;
}
else
{
int iTypes;
ArrayList returnValues=new ArrayList();
if(popTypes[0] == typeof(void))
iTypes=1;
else
iTypes=0;
for(int i=oldTop+1;i<=newTop;i++)
{
returnValues.Add(getAsType(luaState,i,popTypes[iTypes]));
iTypes++;
}
LuaDLL.lua_settop(luaState,oldTop);
return returnValues.ToArray();
}
}
// kevinh - the following line doesn't work for remoting proxies - they always return a match for 'is'
// else if(o is ILuaGeneratedType)
static bool IsILua(object o)
{
if (o is ILuaGeneratedType)
{
// Make sure we are _really_ ILuaGenerated
Type type = o.GetType();
return type.GetInterface("ILuaGeneratedType") != null;
}
return false;
}
/*
* Pushes the object into the Lua stack according to its type.
*/
internal void push(IntPtr luaState, object o)
{
if(o==null)
{
LuaDLL.lua_pushnil(luaState);
}
else if(o is sbyte || o is byte || o is short || o is ushort ||
o is int || o is uint || o is long || o is float ||
o is ulong || o is decimal || o is double)
{
double d=Convert.ToDouble(o);
LuaDLL.lua_pushnumber(luaState,d);
}
else if(o is char)
{
double d = (char)o;
LuaDLL.lua_pushnumber(luaState,d);
}
else if(o is string)
{
string str=(string)o;
LuaDLL.lua_pushstring(luaState,str);
}
else if(o is bool)
{
bool b=(bool)o;
LuaDLL.lua_pushboolean(luaState,b);
}
else if(IsILua(o))
{
(((ILuaGeneratedType)o).__luaInterface_getLuaTable()).push(luaState);
}
else if(o is LuaTable)
{
((LuaTable)o).push(luaState);
}
else if(o is LuaCSFunction)
{
pushFunction(luaState,(LuaCSFunction)o);
}
else if(o is LuaFunction)
{
((LuaFunction)o).push(luaState);
}
else
{
pushObject(luaState,o,"luaNet_metatable");
}
}
/*
* Checks if the method matches the arguments in the Lua stack, getting
* the arguments if it does.
*/
internal bool matchParameters(IntPtr luaState,MethodBase method,ref MethodCache methodCache)
{
return metaFunctions.matchParameters(luaState,method,ref methodCache);
}
internal Array tableToArray(object luaParamValue, Type paramArrayType) {
return metaFunctions.TableToArray(luaParamValue,paramArrayType);
}
}
}

94
LuaInterface/ProxyType.cs Normal file
View File

@@ -0,0 +1,94 @@
using System;
using System.Globalization;
using System.Reflection;
namespace LuaInterface
{
/// <summary>
/// Summary description for ProxyType.
/// </summary>
public class ProxyType : IReflect
{
Type proxy;
public ProxyType(Type proxy)
{
this.proxy = proxy;
}
/// <summary>
/// Provide human readable short hand for this proxy object
/// </summary>
/// <returns></returns>
public override string ToString()
{
return "ProxyType(" + UnderlyingSystemType + ")";
}
public Type UnderlyingSystemType
{
get
{
return proxy;
}
}
public FieldInfo GetField(string name, BindingFlags bindingAttr)
{
return proxy.GetField(name, bindingAttr);
}
public FieldInfo[] GetFields(BindingFlags bindingAttr)
{
return proxy.GetFields(bindingAttr);
}
public MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
{
return proxy.GetMember(name, bindingAttr);
}
public MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
return proxy.GetMembers(bindingAttr);
}
public MethodInfo GetMethod(string name, BindingFlags bindingAttr)
{
return proxy.GetMethod(name, bindingAttr);
}
public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
{
return proxy.GetMethod(name, bindingAttr, binder, types, modifiers);
}
public MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
return proxy.GetMethods(bindingAttr);
}
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr)
{
return proxy.GetProperty(name, bindingAttr);
}
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
{
return proxy.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
}
public PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
return proxy.GetProperties(bindingAttr);
}
public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
return proxy.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
}
}
}

View File

@@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Irc", "OpenRA.Irc\Op
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Renderer.Sdl2", "OpenRA.Renderer.Sdl2\OpenRA.Renderer.Sdl2.csproj", "{33D03738-C154-4028-8EA8-63A3C488A651}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuaInterface", "LuaInterface\LuaInterface.csproj", "{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -184,6 +186,14 @@ Global
{85B48234-8B31-4BE6-AF9C-665CC6866841}.Release|Any CPU.Build.0 = Release|Any CPU
{85B48234-8B31-4BE6-AF9C-665CC6866841}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{85B48234-8B31-4BE6-AF9C-665CC6866841}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Any CPU.Build.0 = Debug|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Mixed Platforms.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = OpenRA.Game\OpenRA.Game.csproj