823 lines
27 KiB
C#
823 lines
27 KiB
C#
namespace LuaInterface
|
|
{
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Reflection;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using Lua511;
|
|
|
|
/*
|
|
* 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 registerTableFunction,unregisterTableFunction,getMethodSigFunction,
|
|
getConstructorSigFunction,importTypeFunction,loadAssemblyFunction;
|
|
|
|
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>();
|
|
|
|
importTypeFunction=new LuaCSFunction(this.importType);
|
|
loadAssemblyFunction=new LuaCSFunction(this.loadAssembly);
|
|
registerTableFunction=new LuaCSFunction(this.registerTable);
|
|
unregisterTableFunction=new LuaCSFunction(this.unregisterTable);
|
|
getMethodSigFunction=new LuaCSFunction(this.getMethodSignature);
|
|
getConstructorSigFunction=new LuaCSFunction(this.getConstructorSignature);
|
|
|
|
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");
|
|
}
|
|
/*
|
|
* 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 = null;
|
|
|
|
try
|
|
{
|
|
assembly = Assembly.LoadWithPartialName(assemblyName);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
// The assemblyName was invalid. It is most likely a path.
|
|
}
|
|
|
|
if (assembly == null)
|
|
{
|
|
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;
|
|
}
|
|
/*
|
|
* 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 typ = o.GetType();
|
|
|
|
return (typ.GetInterface("ILuaGeneratedType") != null);
|
|
}
|
|
else
|
|
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(Func<int, object> luaParamValue, Type paramArrayType, int startIndex, int count) {
|
|
return metaFunctions.TableToArray(luaParamValue,paramArrayType, startIndex, count);
|
|
}
|
|
}
|
|
} |