// 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 .
// #define DEBUG_D_PRE_CALL
// #define DEBUG_D_POS_CALL
namespace UniLua
{
using ULDebug = UniLua.Tools.ULDebug;
using InstructionPtr = Pointer;
using Exception = System.Exception;
public class LuaRuntimeException : Exception
{
public ThreadStatus ErrCode { get; private set; }
public LuaRuntimeException( ThreadStatus errCode )
{
ErrCode = errCode;
}
}
public partial class LuaState
{
internal void D_Throw( ThreadStatus errCode )
{
throw new LuaRuntimeException( errCode );
}
private ThreadStatus D_RawRunProtected( PFuncDelegate func, ref T ud )
{
int oldNumCSharpCalls = NumCSharpCalls;
ThreadStatus res = ThreadStatus.LUA_OK;
try
{
func(ref ud);
}
catch( LuaRuntimeException e )
{
NumCSharpCalls = oldNumCSharpCalls;
res = e.ErrCode;
}
NumCSharpCalls = oldNumCSharpCalls;
return res;
}
private void SetErrorObj( ThreadStatus errCode, StkId oldTop )
{
switch( errCode )
{
case ThreadStatus.LUA_ERRMEM: // memory error?
oldTop.V.SetSValue("not enough memory");
break;
case ThreadStatus.LUA_ERRERR:
oldTop.V.SetSValue("error in error handling");
break;
default: // error message on current top
oldTop.V.SetObj(ref Stack[Top.Index-1].V);
break;
}
Top = Stack[oldTop.Index+1];
}
private ThreadStatus D_PCall( PFuncDelegate func, ref T ud,
int oldTopIndex, int errFunc )
{
int oldCIIndex = CI.Index;
bool oldAllowHook = AllowHook;
int oldNumNonYieldable = NumNonYieldable;
int oldErrFunc = ErrFunc;
ErrFunc = errFunc;
ThreadStatus status = D_RawRunProtected( func, ref ud );
if( status != ThreadStatus.LUA_OK ) // an error occurred?
{
F_Close( Stack[oldTopIndex] );
SetErrorObj( status, Stack[oldTopIndex] );
CI = BaseCI[oldCIIndex];
AllowHook = oldAllowHook;
NumNonYieldable = oldNumNonYieldable;
}
ErrFunc = oldErrFunc;
return status;
}
private void D_Call( StkId func, int nResults, bool allowYield )
{
if( ++NumCSharpCalls >= LuaLimits.LUAI_MAXCCALLS )
{
if( NumCSharpCalls == LuaLimits.LUAI_MAXCCALLS )
G_RunError( "CSharp Stack Overflow" );
else if( NumCSharpCalls >=
(LuaLimits.LUAI_MAXCCALLS + (LuaLimits.LUAI_MAXCCALLS>>3))
)
D_Throw( ThreadStatus.LUA_ERRERR );
}
if( !allowYield )
NumNonYieldable++;
if( !D_PreCall( func, nResults ) ) // is a Lua function ?
V_Execute();
if( !allowYield )
NumNonYieldable--;
NumCSharpCalls--;
}
///
/// return true if function has been executed
///
private bool D_PreCall( StkId func, int nResults )
{
// prepare for Lua call
#if DEBUG_D_PRE_CALL
Console.WriteLine( "============================ D_PreCall func:" + func );
#endif
int funcIndex = func.Index;
if(!func.V.TtIsFunction()) {
// not a function
// retry with `function' tag method
func = tryFuncTM( func );
// now it must be a function
return D_PreCall( func, nResults );
}
if(func.V.ClIsLuaClosure()) {
var cl = func.V.ClLValue();
Utl.Assert(cl != null);
var p = cl.Proto;
D_CheckStack(p.MaxStackSize + p.NumParams);
func = Stack[funcIndex];
//
int n = (Top.Index - func.Index) - 1;
for( ; n 0 )
{
#if DEBUG_D_POS_CALL
Console.WriteLine( "[D] ==== PosCall new LuaNil()" );
#endif
Stack[resIndex++].V.SetNilValue();
}
Top = Stack[resIndex];
#if DEBUG_D_POS_CALL
Console.WriteLine( "[D] ==== PosCall return " + (wanted - LuaDef.LUA_MULTRET) );
#endif
return (wanted - LuaDef.LUA_MULTRET);
}
private CallInfo ExtendCI()
{
int newIndex = CI.Index + 1;
if(newIndex >= BaseCI.Length) {
int newLength = BaseCI.Length*2;
var newBaseCI = new CallInfo[newLength];
int i = 0;
while(i < BaseCI.Length) {
newBaseCI[i] = BaseCI[i];
newBaseCI[i].List = newBaseCI;
++i;
}
while(i < newLength) {
var newCI = new CallInfo();
newBaseCI[i] = newCI;
newCI.List = newBaseCI;
newCI.Index = i;
++i;
}
BaseCI = newBaseCI;
CI = newBaseCI[CI.Index];
return newBaseCI[newIndex];
}
else {
return BaseCI[newIndex];
}
}
private int AdjustVarargs( LuaProto p, int actual )
{
// `...'
// : func (base)fixed-p1 fixed-p2 var-p1 var-p2 top
// : func nil nil var-p1 var-p2 (base)fixed-p1 fixed-p2 (reserved...) top
//
// `...'
// func (base)fixed-p1 fixed-p2 (reserved...) top
int NumFixArgs = p.NumParams;
Utl.Assert( actual >= NumFixArgs, "AdjustVarargs (actual >= NumFixArgs) is false" );
int fixedArg = Top.Index - actual; // first fixed argument
int stackBase = Top.Index; // final position of first argument
for( int i=stackBase; ifunc.Index; --i)
{ Stack[i].V.SetObj(ref Stack[i-1].V); }
IncrTop();
func.V.SetObj(ref tmObj.V);
return func;
}
private void D_CheckStack(int n)
{
if(StackLast - Top.Index <= n)
D_GrowStack(n);
// TODO: FOR DEBUGGING
// else
// CondMoveStack();
}
// some space for error handling
private const int ERRORSTACKSIZE = LuaConf.LUAI_MAXSTACK + 200;
private void D_GrowStack(int n)
{
int size = Stack.Length;
if(size > LuaConf.LUAI_MAXSTACK)
D_Throw(ThreadStatus.LUA_ERRERR);
int needed = Top.Index + n + LuaDef.EXTRA_STACK;
int newsize = 2 * size;
if(newsize > LuaConf.LUAI_MAXSTACK)
{ newsize = LuaConf.LUAI_MAXSTACK; }
if(newsize < needed)
{ newsize = needed; }
if(newsize > LuaConf.LUAI_MAXSTACK)
{
D_ReallocStack(ERRORSTACKSIZE);
G_RunError("stack overflow");
}
else
{
D_ReallocStack(newsize);
}
}
private void D_ReallocStack(int size)
{
Utl.Assert(size <= LuaConf.LUAI_MAXSTACK || size == ERRORSTACKSIZE);
var newStack = new StkId[size];
int i = 0;
for( ; i