// The CMLeonOS Project (https://github.com/Leonmmcoset/CMLeonOS)
// Copyright (C) 2025-present LeonOS 2 Developer Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
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
}
}
}