namespace UniLua { public class LuaDebug { public string Name; public string NameWhat; public int ActiveCIIndex; public int CurrentLine; public int NumUps; public bool IsVarArg; public int NumParams; public bool IsTailCall; public string Source; public int LineDefined; public int LastLineDefined; public string What; public string ShortSrc; } public partial class LuaState { bool ILuaAPI.GetStack( int level, LuaDebug ar ) { if( level < 0 ) return false; int index; for( index = CI.Index; level > 0 && index > 0; --index ) { level--; } bool status = false; if( level == 0 && index > 0 ) { status = true; ar.ActiveCIIndex = index; } return status; } public int GetInfo( string what, LuaDebug ar ) { CallInfo ci; StkId func; int pos = 0; if( what[pos] == '>' ) { ci = null; func = Stack[Top.Index - 1]; Utl.ApiCheck(func.V.TtIsFunction(), "function expected"); pos++; Top = Stack[Top.Index-1]; } else { ci = BaseCI[ar.ActiveCIIndex]; func = Stack[ci.FuncIndex]; Utl.Assert(Stack[ci.FuncIndex].V.TtIsFunction()); } // var IsClosure( func.Value ) ? func.Value int status = AuxGetInfo( what, ar, func, ci ); if( what.Contains( "f" ) ) { Top.V.SetObj(ref func.V); IncrTop(); } if( what.Contains( "L" ) ) { CollectValidLines( func ); } return status; } private int AuxGetInfo( string what, LuaDebug ar, StkId func, CallInfo ci ) { int status = 1; for( int i=0; i LuaDef.LUA_IDSIZE ) { ar.ShortSrc = ar.Source.Substring(0, LuaDef.LUA_IDSIZE); } else ar.ShortSrc = ar.Source; } private void AddInfo( string msg ) { // var api = (ILuaAPI)this; // TODO if( CI.IsLua ) { var line = GetCurrentLine(CI); var src = GetCurrentLuaFunc(CI).Proto.Source; if( src == null ) src = "?"; // PushString, PushString API // API ApiIncrTop Top CI.Top // api.PushString( msg ); O_PushString( string.Format( "{0}:{1}: {2}", src, line, msg ) ); } } internal void G_RunError( string fmt, params object[] args ) { AddInfo( string.Format( fmt, args ) ); G_ErrorMsg(); } private void G_ErrorMsg() { if( ErrFunc != 0 ) // is there an error handling function? { StkId errFunc = RestoreStack( ErrFunc ); if(!errFunc.V.TtIsFunction()) D_Throw( ThreadStatus.LUA_ERRERR ); var below = Stack[Top.Index-1]; Top.V.SetObj(ref below.V); below.V.SetObj(ref errFunc.V); IncrTop(); D_Call( below, 1, false ); } D_Throw( ThreadStatus.LUA_ERRRUN ); } private string UpvalName( LuaProto p, int uv ) { // TODO return "(UpvalName:NotImplemented)"; } private string GetUpvalueName( CallInfo ci, StkId o, out string name ) { var func = Stack[ci.FuncIndex]; Utl.Assert(func.V.TtIsFunction() && func.V.ClIsLuaClosure()); var lcl = func.V.ClLValue(); for(int i=0; i= a+2 ) setreg = pc; break; } case OpCode.OP_CALL: case OpCode.OP_TAILCALL: { // effect all registers above base if( reg >= a ) setreg = pc; break; } case OpCode.OP_JMP: { var b = ins.GETARG_sBx(); var dest = pc + 1 + b; // jump is forward and do not skip `lastpc' if( pc < dest && dest <= lastpc ) pc += b; // do the jump break; } case OpCode.OP_TEST: { // jumped code can change `a' if( reg == a ) setreg = pc; break; } default: { // any instruction that set A if( Coder.TestAMode( op ) && reg == a ) { setreg = pc; } break; } } } return setreg; } private string GetObjName( LuaProto proto, int lastpc, int reg, out string name ) { name = F_GetLocalName( proto, reg+1, lastpc ); if( name != null ) // is a local? return "local"; // else try symbolic execution var pc = FindSetReg( proto, lastpc, reg ); if( pc != -1 ) { var ins = proto.Code[pc]; var op = ins.GET_OPCODE(); switch( op ) { case OpCode.OP_MOVE: { var b = ins.GETARG_B(); // move from `b' to `a' if( b < ins.GETARG_A() ) return GetObjName(proto, pc, b, out name); break; } case OpCode.OP_GETTABUP: case OpCode.OP_GETTABLE: { var k = ins.GETARG_C(); var t = ins.GETARG_B(); var vn = (op == OpCode.OP_GETTABLE) ? F_GetLocalName( proto, t+1, pc ) : UpvalName( proto, t ); KName( proto, pc, k, out name ); return (vn == LuaDef.LUA_ENV) ? "global" : "field"; } case OpCode.OP_GETUPVAL: { name = UpvalName( proto, ins.GETARG_B() ); return "upvalue"; } case OpCode.OP_LOADK: case OpCode.OP_LOADKX: { var b = (op == OpCode.OP_LOADK) ? ins.GETARG_Bx() : proto.Code[pc+1].GETARG_Ax(); var val = proto.K[b]; if(val.V.TtIsString()) { name = val.V.SValue(); return "constant"; } break; } case OpCode.OP_SELF: { var k = ins.GETARG_C(); // key index KName( proto, pc, k, out name ); return "method"; } default: break; // go through to return null } } return null; // could not find reasonable name } private bool IsInStack( CallInfo ci, StkId o ) { // TODO return false; } private void G_SimpleTypeError( ref TValue o, string op ) { string t = ObjTypeName( ref o ); G_RunError( "attempt to {0} a {1} value", op, t ); } private void G_TypeError( StkId o, string op ) { CallInfo ci = CI; string name = null; string kind = null; string t = ObjTypeName(ref o.V); if( ci.IsLua ) { kind = GetUpvalueName( ci, o, out name); if( kind != null && IsInStack( ci, o ) ) { var lcl = Stack[ci.FuncIndex].V.ClLValue(); kind = GetObjName( lcl.Proto, ci.CurrentPc, (o.Index - ci.BaseIndex), out name ); } } if( kind != null ) G_RunError( "attempt to {0} {1} '{2}' (a {3} value)", op, kind, name, t ); else G_RunError( "attempt to {0} a {1} value", op, t ); } private void G_ArithError( StkId p1, StkId p2 ) { var n = new TValue(); if( !V_ToNumber( p1, ref n ) ) { p2 = p1; } // first operand is wrong G_TypeError( p2, "perform arithmetic on" ); } private void G_OrderError( StkId p1, StkId p2 ) { string t1 = ObjTypeName(ref p1.V); string t2 = ObjTypeName(ref p2.V); if( t1 == t2 ) G_RunError( "attempt to compare two {0} values", t1 ); else G_RunError( "attempt to compare {0} with {1}", t1, t2 ); } private void G_ConcatError( StkId p1, StkId p2 ) { // TODO } } }