/*
* This file is part of LuaInterface.
*
* Copyright (C) 2003-2005 Fabio Mascarenhas de Queiroz.
* Copyright (C) 2012 Megax
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.IO;
using System.Threading;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using LuaInterface.Event;
using LuaInterface.Method;
using LuaInterface.Exceptions;
using LuaInterface.Extensions;
namespace LuaInterface
{
using LuaCore = KeraLua.Lua;
/*
* 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)
* */
[CLSCompliant(true)]
public class Lua : IDisposable
{
#region lua debug functions
///
/// Event that is raised when an exception occures during a hook call.
///
/// Reinhard Ostermeier
public event EventHandler HookException;
///
/// Event when lua hook callback is called
///
///
/// Is only raised if SetDebugHook is called before.
///
/// Reinhard Ostermeier
public event EventHandler DebugHook;
///
/// lua hook calback delegate
///
/// Reinhard Ostermeier
private LuaCore.lua_Hook hookCallback = null;
#endregion
#region Globals auto-complete
private readonly List globals = new List ();
private bool globalsSorted;
#endregion
private /*readonly */ LuaCore.lua_State luaState;
///
/// True while a script is being executed
///
public bool IsExecuting { get { return executing; } }
private LuaCore.lua_CFunction panicCallback;
private ObjectTranslator translator;
///
/// Used to ensure multiple .net threads all get serialized by this single lock for access to the lua stack/objects
///
//private object luaLock = new object();
private bool _StatePassed;
private bool executing;
static string init_luanet =
"local metatable = {} \n" +
"local import_type = luanet.import_type \n" +
"local load_assembly = luanet.load_assembly \n" +
" \n" +
"-- Lookup a .NET identifier component. \n" +
"function metatable:__index(key) -- key is e.g. \"Form\" \n" +
" -- Get the fully-qualified name, e.g. \"System.Windows.Forms.Form\" \n" +
" local fqn = ((rawget(self, \".fqn\") and rawget(self, \".fqn\") .. \n" +
" \".\") or \"\") .. key \n" +
" \n" +
" -- Try to find either a luanet function or a CLR type \n" +
" local obj = rawget(luanet, key) or import_type(fqn) \n" +
" \n" +
" -- If key is neither a luanet function or a CLR type, then it is simply \n" +
" -- an identifier component. \n" +
" if obj == nil then \n" +
" -- It might be an assembly, so we load it too. \n" +
" load_assembly(fqn) \n" +
" obj = { [\".fqn\"] = fqn } \n" +
" setmetatable(obj, metatable) \n" +
" end \n" +
" \n" +
" -- Cache this lookup \n" +
" rawset(self, key, obj) \n" +
" return obj \n" +
"end \n" +
" \n" +
"-- A non-type has been called; e.g. foo = System.Foo() \n" +
"function metatable:__call(...) \n" +
" error(\"No such type: \" .. rawget(self, \".fqn\"), 2) \n" +
"end \n" +
" \n" +
"-- This is the root of the .NET namespace \n" +
"luanet[\".fqn\"] = false \n" +
"setmetatable(luanet, metatable) \n" +
" \n" +
"-- Preload the mscorlib assembly \n" +
"luanet.load_assembly(\"mscorlib\") \n";
#region Globals auto-complete
///
/// An alphabetically sorted list of all globals (objects, methods, etc.) externally added to this Lua instance
///
/// Members of globals are also listed. The formatting is optimized for text input auto-completion.
public IEnumerable Globals {
get {
// Only sort list when necessary
if (!globalsSorted) {
globals.Sort ();
globalsSorted = true;
}
return globals;
}
}
#endregion
public Lua ()
{
luaState = LuaLib.luaL_newstate (); // steffenj: Lua 5.1.1 API change (lua_open is gone)
//LuaLib.luaopen_base(luaState); // steffenj: luaopen_* no longer used
LuaLib.luaL_openlibs (luaState); // steffenj: Lua 5.1.1 API change (luaopen_base is gone, just open all libs right here)
LuaLib.lua_pushstring (luaState, "LUAINTERFACE LOADED");
LuaLib.lua_pushboolean (luaState, true);
LuaLib.lua_settable (luaState, (int)LuaIndexes.Registry);
LuaLib.lua_newtable (luaState);
LuaLib.lua_setglobal (luaState, "luanet");
LuaLib.lua_pushvalue (luaState, (int)LuaIndexes.Globals);
LuaLib.lua_getglobal (luaState, "luanet");
LuaLib.lua_pushstring (luaState, "getmetatable");
LuaLib.lua_getglobal (luaState, "getmetatable");
LuaLib.lua_settable (luaState, -3);
LuaLib.lua_replace (luaState, (int)LuaIndexes.Globals);
translator = new ObjectTranslator (this, luaState);
LuaLib.lua_replace (luaState, (int)LuaIndexes.Globals);
LuaLib.luaL_dostring (luaState, Lua.init_luanet); // steffenj: lua_dostring renamed to luaL_dostring
// We need to keep this in a managed reference so the delegate doesn't get garbage collected
panicCallback = new LuaCore.lua_CFunction (PanicCallback);
LuaLib.lua_atpanic (luaState, panicCallback);
//LuaLib.lua_atlock(luaState, lockCallback = new LuaCore.lua_CFunction(LockCallback));
//LuaLib.lua_atunlock(luaState, unlockCallback = new LuaCore.lua_CFunction(UnlockCallback));
}
/*
* CAUTION: LuaInterface.Lua instances can't share the same lua state!
*/
public Lua (LuaCore.lua_State lState)
{
LuaLib.lua_pushstring (lState, "LUAINTERFACE LOADED");
LuaLib.lua_gettable (lState, (int)LuaIndexes.Registry);
if (LuaLib.lua_toboolean (lState, -1)) {
LuaLib.lua_settop (lState, -2);
throw new LuaException ("There is already a LuaInterface.Lua instance associated with this Lua state");
} else {
LuaLib.lua_settop (lState, -2);
LuaLib.lua_pushstring (lState, "LUAINTERFACE LOADED");
LuaLib.lua_pushboolean (lState, true);
LuaLib.lua_settable (lState, (int)LuaIndexes.Registry);
luaState = lState;
LuaLib.lua_pushvalue (lState, (int)LuaIndexes.Globals);
LuaLib.lua_getglobal (lState, "luanet");
LuaLib.lua_pushstring (lState, "getmetatable");
LuaLib.lua_getglobal (lState, "getmetatable");
LuaLib.lua_settable (lState, -3);
LuaLib.lua_replace (lState, (int)LuaIndexes.Globals);
translator = new ObjectTranslator (this, luaState);
LuaLib.lua_replace (lState, (int)LuaIndexes.Globals);
LuaLib.luaL_dostring (lState, Lua.init_luanet); // steffenj: lua_dostring renamed to luaL_dostring
}
_StatePassed = true;
}
///
/// Called for each lua_lock call
///
///
/// Not yet used
/*int LockCallback(LuaCore.lua_State luaState)
{
// Monitor.Enter(luaLock);
return 0;
}*/
///
/// Called for each lua_unlock call
///
///
/// Not yet used
/*int UnlockCallback(LuaCore.lua_State luaState)
{
// Monitor.Exit(luaLock);
return 0;
}*/
public void Close ()
{
if (_StatePassed)
return;
////// if(luaState != LuaCore.lua_State.Zero)
if (!luaState.IsNull ())
LuaCore.lua_close (luaState);
//luaState = LuaCore.lua_State.Zero; <- suggested by Christopher Cebulski http://luaforge.net/forum/forum.php?thread_id = 44593&forum_id = 146
}
#if MONOTOUCH
[MonoTouch.MonoPInvokeCallback (typeof (LuaCore.lua_CFunction))]
#endif
[System.Runtime.InteropServices.AllowReversePInvokeCalls]
static int PanicCallback (LuaCore.lua_State luaState)
{
// string desc = LuaLib.lua_tostring(luaState, 1);
string reason = string.Format ("unprotected error in call to Lua API ({0})", LuaLib.lua_tostring (luaState, -1));
// lua_tostring(L, -1);
throw new LuaException (reason);
}
///
/// Assuming we have a Lua error string sitting on the stack, throw a C# exception out to the user's app
///
/// Thrown if the script caused an exception
private void ThrowExceptionFromError (int oldTop)
{
object err = translator.getObject (luaState, -1);
LuaLib.lua_settop (luaState, oldTop);
// A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved)
var luaEx = err as LuaScriptException;
if (!luaEx.IsNull ())
throw luaEx;
// A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it
if (err.IsNull ())
err = "Unknown Lua Error";
throw new LuaScriptException (err.ToString (), string.Empty);
}
///
/// Convert C# exceptions into Lua errors
///
/// num of things on stack
/// null for no pending exception
internal int SetPendingException (Exception e)
{
var caughtExcept = e;
if (!caughtExcept.IsNull ()) {
translator.throwError (luaState, caughtExcept);
LuaLib.lua_pushnil (luaState);
return 1;
} else
return 0;
}
///
///
///
///
///
///
public LuaFunction LoadString (string chunk, string name)
{
int oldTop = LuaLib.lua_gettop (luaState);
executing = true;
try {
if (LuaLib.luaL_loadbuffer (luaState, chunk, name) != 0)
ThrowExceptionFromError (oldTop);
} finally {
executing = false;
}
var result = translator.getFunction (luaState, -1);
translator.popValues (luaState, oldTop);
return result;
}
///
///
///
///
///
public LuaFunction LoadFile (string fileName)
{
int oldTop = LuaLib.lua_gettop (luaState);
if (LuaLib.luaL_loadfile (luaState, fileName) != 0)
ThrowExceptionFromError (oldTop);
var 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)
{
int oldTop = LuaLib.lua_gettop (luaState);
if (LuaLib.luaL_loadbuffer (luaState, chunk, "chunk") == 0) {
executing = true;
try {
if (LuaLib.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
}
///
/// Executes a Lua chnk and returns all the chunk's return values in an array.
///
/// Chunk to execute
/// Name to associate with the chunk
///
public object[] DoString (string chunk, string chunkName)
{
int oldTop = LuaLib.lua_gettop (luaState);
executing = true;
if (LuaLib.luaL_loadbuffer (luaState, chunk, chunkName) == 0) {
try {
if (LuaLib.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
}
/*
* Excutes a Lua file and returns all the chunk's return
* values in an array
*/
public object[] DoFile (string fileName)
{
int oldTop = LuaLib.lua_gettop (luaState);
if (LuaLib.luaL_loadfile (luaState, fileName) == 0) {
executing = true;
try {
if (LuaLib.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
}
/*
* 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 = LuaLib.lua_gettop (luaState);
string[] path = fullPath.Split (new char[] { '.' });
LuaLib.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);
}
LuaLib.lua_settop (luaState, oldTop);
return returnValue;
}
set {
int oldTop = LuaLib.lua_gettop (luaState);
string[] path = fullPath.Split (new char[] { '.' });
if (path.Length == 1) {
translator.push (luaState, value);
LuaLib.lua_setglobal (luaState, fullPath);
} else {
LuaLib.lua_getglobal (luaState, path [0]);
string[] remainingPath = new string[path.Length - 1];
Array.Copy (path, 1, remainingPath, 0, path.Length - 1);
setObject (remainingPath, value);
}
LuaLib.lua_settop (luaState, oldTop);
// Globals auto-complete
if (value.IsNull ()) {
// Remove now obsolete entries
globals.Remove (fullPath);
} else {
// Add new entries
if (!globals.Contains (fullPath))
registerGlobal (fullPath, value.GetType (), 0);
}
}
}
#region Globals auto-complete
///
/// Adds an entry to (recursivley handles 2 levels of members)
///
/// The index accessor path ot the entry
/// The type of the entry
/// How deep have we gone with recursion?
private void registerGlobal (string path, Type type, int recursionCounter)
{
// If the type is a global method, list it directly
if (type == typeof(LuaCore.lua_CFunction)) {
// Format for easy method invocation
globals.Add (path + "(");
}
// If the type is a class or an interface and recursion hasn't been running too long, list the members
else if ((type.IsClass || type.IsInterface) && type != typeof(string) && recursionCounter < 2) {
#region Methods
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance)) {
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 (var 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 (var 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
} else
globals.Add (path); // Otherwise simply add the element to the list
// List will need to be sorted on next access
globalsSorted = false;
}
#endregion
/*
* Navigates a table in the top of the stack, returning
* the value of the specified field
*/
internal object getObject (string[] remainingPath)
{
object returnValue = null;
for (int i = 0; i < remainingPath.Length; i++) {
LuaLib.lua_pushstring (luaState, remainingPath [i]);
LuaLib.lua_gettable (luaState, -2);
returnValue = translator.getObject (luaState, -1);
if (returnValue.IsNull ())
break;
}
return returnValue;
}
/*
* Gets a numeric global variable
*/
public double GetNumber (string fullPath)
{
return (double)this [fullPath];
}
/*
* Gets a string global variable
*/
public string GetString (string fullPath)
{
return this [fullPath].ToString ();
}
/*
* Gets a table global variable
*/
public LuaTable GetTable (string fullPath)
{
return (LuaTable)this [fullPath];
}
/*
* Gets a table global variable as an object implementing
* the interfaceType interface
*/
public object GetTable (Type interfaceType, string fullPath)
{
return CodeGeneration.Instance.GetClassInstance (interfaceType, GetTable (fullPath));
}
/*
* Gets a function global variable
*/
public LuaFunction GetFunction (string fullPath)
{
object obj = this [fullPath];
return (obj is LuaCore.lua_CFunction ? new LuaFunction ((LuaCore.lua_CFunction)obj, this) : (LuaFunction)obj);
}
/*
* Register a delegate type to be used to convert Lua funcitions to C# delegates (useful for iOS where there is no dynamic code generation)
* type delegateType
*/
public void RegisterLuaDelegateType (Type delegateType, Type luaDelegateType)
{
CodeGeneration.Instance.RegisterLuaDelegateType (delegateType, luaDelegateType);
}
public void RegisterLuaClassType (Type klass, Type luaClass)
{
CodeGeneration.Instance.RegisterLuaClassType (klass, luaClass);
}
/*
* 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 = LuaLib.lua_gettop (luaState);
if (!LuaLib.lua_checkstack (luaState, args.Length + 6))
throw new LuaException ("Lua stack overflow");
translator.push (luaState, function);
if (!args.IsNull ()) {
nArgs = args.Length;
for (int i = 0; i < args.Length; i++)
translator.push (luaState, args [i]);
}
executing = true;
try {
int error = LuaLib.lua_pcall (luaState, nArgs, -1, 0);
if (error != 0)
ThrowExceptionFromError (oldTop);
} finally {
executing = false;
}
return !returnTypes.IsNull () ? translator.popValues (luaState, oldTop, returnTypes) : 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++) {
LuaLib.lua_pushstring (luaState, remainingPath [i]);
LuaLib.lua_gettable (luaState, -2);
}
LuaLib.lua_pushstring (luaState, remainingPath [remainingPath.Length - 1]);
translator.push (luaState, val);
LuaLib.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 = LuaLib.lua_gettop (luaState);
if (path.Length == 1) {
LuaLib.lua_newtable (luaState);
LuaLib.lua_setglobal (luaState, fullPath);
} else {
LuaLib.lua_getglobal (luaState, path [0]);
for (int i = 1; i < path.Length-1; i++) {
LuaLib.lua_pushstring (luaState, path [i]);
LuaLib.lua_gettable (luaState, -2);
}
LuaLib.lua_pushstring (luaState, path [path.Length - 1]);
LuaLib.lua_newtable (luaState);
LuaLib.lua_settable (luaState, -3);
}
LuaLib.lua_settop (luaState, oldTop);
}
public ListDictionary GetTableDict (LuaTable table)
{
var dict = new ListDictionary ();
int oldTop = LuaLib.lua_gettop (luaState);
translator.push (luaState, table);
LuaLib.lua_pushnil (luaState);
while (LuaLib.lua_next(luaState, -2) != 0) {
dict [translator.getObject (luaState, -2)] = translator.getObject (luaState, -1);
LuaLib.lua_settop (luaState, -2);
}
LuaLib.lua_settop (luaState, oldTop);
return dict;
}
/*
* Lets go of a previously allocated reference to a table, function
* or userdata
*/
#region lua debug functions
///
/// Activates the debug hook
///
/// Mask
/// Count
/// see lua docs. -1 if hook is already set
/// Reinhard Ostermeier
public int SetDebugHook (EventMasks mask, int count)
{
if (hookCallback.IsNull ()) {
hookCallback = new LuaCore.lua_Hook (DebugHookCallback);
return LuaCore.lua_sethook (luaState, hookCallback, (int)mask, count);
}
return -1;
}
///
/// Removes the debug hook
///
/// see lua docs
/// Reinhard Ostermeier
public int RemoveDebugHook ()
{
hookCallback = null;
return LuaCore.lua_sethook (luaState, null, 0, 0);
}
///
/// Gets the hook mask.
///
/// hook mask
/// Reinhard Ostermeier
public EventMasks GetHookMask ()
{
return (EventMasks)LuaCore.lua_gethookmask (luaState);
}
///
/// Gets the hook count
///
/// see lua docs
/// Reinhard Ostermeier
public int GetHookCount ()
{
return LuaCore.lua_gethookcount (luaState);
}
///
/// Gets the stack entry on a given level
///
/// level
/// lua debug structure
/// Returns true if level was allowed, false if level was invalid.
/// Reinhard Ostermeier
/*public bool GetStack(int level, out LuaCore.lua_Debug luaDebug)
{
luaDebug = new LuaDebug();
LuaCore.lua_State ld = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(luaDebug));
System.Runtime.InteropServices.Marshal.StructureToPtr(luaDebug, ld, false);
try
{
return LuaLib.lua_getstack(luaState, level, luaDebug) != 0;
}
finally
{
luaDebug = (LuaDebug)System.Runtime.InteropServices.Marshal.PtrToStructure(ld, typeof(LuaDebug));
System.Runtime.InteropServices.Marshal.FreeHGlobal(ld);
}
}*/
///
/// Gets info (see lua docs)
///
/// what (see lua docs)
/// lua debug structure
/// see lua docs
/// Reinhard Ostermeier
/*public int GetInfo(String what, ref LuaCore.lua_Debug luaDebug)
{
LuaCore.lua_State ld = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(luaDebug));
System.Runtime.InteropServices.Marshal.StructureToPtr(luaDebug, ld, false);
try
{
return LuaLib.lua_getinfo(luaState, what, ld);
}
finally
{
luaDebug = (LuaDebug)System.Runtime.InteropServices.Marshal.PtrToStructure(ld, typeof(LuaDebug));
System.Runtime.InteropServices.Marshal.FreeHGlobal(ld);
}
}*/
///
/// Gets local (see lua docs)
///
/// lua debug structure
/// see lua docs
/// see lua docs
/// Reinhard Ostermeier
public string GetLocal (LuaCore.lua_Debug luaDebug, int n)
{
return LuaCore.lua_getlocal (luaState, luaDebug, n).ToString ();
}
///
/// Sets local (see lua docs)
///
/// lua debug structure
/// see lua docs
/// see lua docs
/// Reinhard Ostermeier
public string SetLocal (LuaCore.lua_Debug luaDebug, int n)
{
return LuaCore.lua_setlocal (luaState, luaDebug, n).ToString ();
}
///
/// Gets up value (see lua docs)
///
/// see lua docs
/// see lua docs
/// see lua docs
/// Reinhard Ostermeier
public string GetUpValue (int funcindex, int n)
{
return LuaCore.lua_getupvalue (luaState, funcindex, n).ToString ();
}
///
/// Sets up value (see lua docs)
///
/// see lua docs
/// see lua docs
/// see lua docs
/// Reinhard Ostermeier
public string SetUpValue (int funcindex, int n)
{
return LuaCore.lua_setupvalue (luaState, funcindex, n).ToString ();
}
///
/// Delegate that is called on lua hook callback
///
/// lua state
/// Pointer to LuaDebug (lua_debug) structure
/// Reinhard Ostermeier
///
#if MONOTOUCH
[MonoTouch.MonoPInvokeCallback (typeof (LuaCore.lua_Hook))]
#endif
[System.Runtime.InteropServices.AllowReversePInvokeCalls]
private void DebugHookCallback (LuaCore.lua_State luaState, LuaCore.lua_Debug luaDebug)
{
try {
var temp = DebugHook;
if (!temp.IsNull ())
temp (this, new DebugHookEventArgs (luaDebug));
} catch (Exception ex) {
OnHookException (new HookExceptionEventArgs (ex));
}
}
private void OnHookException (HookExceptionEventArgs e)
{
var temp = HookException;
if (!temp.IsNull ())
temp (this, e);
}
///
/// Pops a value from the lua stack.
///
/// Returns the top value from the lua stack.
/// Reinhard Ostermeier
public object Pop ()
{
int top = LuaLib.lua_gettop (luaState);
return translator.popValues (luaState, top - 1) [0];
}
///
/// Pushes a value onto the lua stack.
///
/// Value to push.
/// Reinhard Ostermeier
public void Push (object value)
{
translator.push (luaState, value);
}
#endregion
internal void dispose (int reference)
{
if (!luaState.IsNull ()) //Fix submitted by Qingrui Li
LuaLib.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 = LuaLib.lua_gettop (luaState);
LuaLib.lua_getref (luaState, reference);
LuaLib.lua_pushstring (luaState, field);
LuaLib.lua_rawget (luaState, -2);
object obj = translator.getObject (luaState, -1);
LuaLib.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 = LuaLib.lua_gettop (luaState);
LuaLib.lua_getref (luaState, reference);
object returnValue = getObject (field.Split (new char[] {'.'}));
LuaLib.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 = LuaLib.lua_gettop (luaState);
LuaLib.lua_getref (luaState, reference);
translator.push (luaState, field);
LuaLib.lua_gettable (luaState, -2);
object returnValue = translator.getObject (luaState, -1);
LuaLib.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 = LuaLib.lua_gettop (luaState);
LuaLib.lua_getref (luaState, reference);
setObject (field.Split (new char[] {'.'}), val);
LuaLib.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 = LuaLib.lua_gettop (luaState);
LuaLib.lua_getref (luaState, reference);
translator.push (luaState, field);
translator.push (luaState, val);
LuaLib.lua_settable (luaState, -3);
LuaLib.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 = LuaLib.lua_gettop (luaState);
var wrapper = new LuaMethodWrapper (translator, target, function.DeclaringType, function);
translator.push (luaState, new LuaCore.lua_CFunction (wrapper.invokeFunction));
this [path] = translator.getObject (luaState, -1);
var f = GetFunction (path);
LuaLib.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 = LuaLib.lua_gettop (luaState);
LuaLib.lua_getref (luaState, ref1);
LuaLib.lua_getref (luaState, ref2);
int equal = LuaLib.lua_equal (luaState, -1, -2);
LuaLib.lua_settop (luaState, top);
return (equal != 0);
}
internal void pushCSFunction (LuaCore.lua_CFunction function)
{
translator.pushFunction (luaState, function);
}
#region IDisposable Members
public virtual void Dispose ()
{
if (!translator.IsNull ()) {
translator.pendingEvents.Dispose ();
translator = null;
}
this.Close ();
GC.Collect ();
GC.WaitForPendingFinalizers ();
}
#endregion
}
}